Callback与Promise,将setTimeout封装成Promise
- 为什么回调地狱(Callback hell)会发生?
- Promise披着怎样的面纱?
首先Promise的出现,是为了将困在Callback hell中的芸芸众生解救出来,相信回调地狱是JavaScript开发人员最头疼的问题之一吧!尤其在接手前人的代码时,打开看到满满的Callback嵌套,是不是很想递出辞职信;那么Promise是如何解救痛苦中的芸芸众生的呢!
目录
1.回调函数的顺序与回调地狱
2.定时器
3.网络请求
4.Promise
5.sleep函数
回调函数的顺序与回调地狱
我们那火影举例(相信在座的大部分都看过):如果你是卡卡西的话,当敌人太多时就要开写轮眼、写轮眼打开后,快嗝屁之前要使用千鸟、千鸟用完之后必须注意影分身;
下面用伪代码表示就是:
卡卡西.addEventListener('敌人太多', function(){
卡卡西.addEventListener('快嗝屁了',function(){
写轮眼();
卡卡西.addEventListener('写轮眼开完了',function(){
跑路();
})
})
})
以上的举例就是常见的回调地狱了(Callback hell),这也是很多人在办公室处理的事情太多的时候,常常会做到,不知道自己在做什么的原因。
为了不过度嵌套太多的注册事件、回调函数,后来开发者想到了各种的pattern,最后promise被纳入了ES6的标准里;
定时器
定时器是JavaScript中一个很常见的方法,而它的语法和前面的注册事件相当相似
var timeoutID = scope.setTimeout(code[, delay]);
第一个参数正是上面说到的回调函数(Callback),第二个参数则是微秒数;
举一个例子:
假设你的室友是一个很喜欢同时操作很多个session的肥宅,有一天这位肥宅和你说:“诶,十分钟后帮我开一下电脑,我要连回去,拜托!”
setTimeout(() => 开电脑, 10*60*1000);
内置的定时器会在十分钟之后去触发这个事件;
注意:clearSetTimeout(timerId)
可以把定时器事件停止
通常这个肥宅不会这么轻易的就放过你,他还会对你说:“打开后两分钟帮我连上Steam,然后帮我下个游戏。”
setTimeout(()=>{
打开电脑();
you.addEventListener('opened',()=>{
setTimeout(()=>{
连上steam();
},2*60*1000)
})
},10*60*1000)
网络请求
在JavaScript中我们不希望一个操作太久,就好比一个顾客点餐如果花了十分钟,这段时间内服务员就无法去服务其他的客人。所以许多长时间的操作,如IO、network请求,一般会使用异步的处理方式。
网络请求主要分为两种:XMLHTTPRequest(有名的ajax就是针对此封装的),以及后来的fetch。而XHR主要为请求的事件注册load
事件来监听响应(response)。最简单的做法就是addEventListener
function reqListener () {
console.log(this.responseText);
}
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "http://www.example.org/example.txt");
oReq.send();
后来的fetch标准则是用到了then方法的链式调用。
fetch('http://example.com/movies.json')
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(myJson);
});
这个then是由promise的原型所提供的,fetch则是promise的实体;
Promise
Promise(承诺),承诺有可能信守承诺(resolve)、承诺失败(reject)。在Promise的结果出现前,会进入一个不知道是成功还是失败的状态(Pending)。
Promise ->pending->(resolve/reject)
幸运的是Promise的API非常的友好,可以通过then和catch这两个方法,把回调函数放进去,针对两个最终结构进行处理;
function success(){
// do something when success
}
function fail(){
// do something when fail
}
(Statement will pending)
.then(success)
.catch(fail)
最后不管是then还是catch,都会把Callback给Promise化。
fetch('https://your-domain/api.json')
.then(res=>res.json()) /* 这也是异步 */
.then(j=>console.log(j.id)) /* json() resolve 之后 */
.catch(()=>alert('fail'))
sleep函数(封装setTimeout)
传统的setTimeout
setTimeout( ()=>{
} , 1000)
我们希望这个能暂停一段时间并做一些相关的操作,但是又不想写太过嵌套的回调函数
/* 最终希望的函数 sleep */
sleep(5).then(()=>console.log('5秒过去'))
进一步修改
function sleep(sec){
setTimeout(()=>{
},sec*1000)
}
初步应该是这样,但是没有Promise依然无法使用then方法
function sleep(sec){
return new Promise((resolve,reject)=>{
setTimeout(()=>resolve(),sec*1000)
})
在setTimeout触发之前,sleep函数会处于pending状态;但是reject任然没有派上用场,所以在这里增加了一个特性,超过十秒的话就reject掉;
/* 想象中的sleep */
sleep(5).then(()=>{
})
sleep(11).then(()=>{
}).catch(e=>console.log(e)) // Error:睡太久了八?
结合以上修改就是:
function sleep(sec){
return new Promise((resolve,reject)=>{
if(sec > 10) reject(new Error('睡太久了八!'));
setTimeout(()=>resolve(),sec*1000)
})
}
sleep(11).catch(e=>console.log(e)) // Error: 睡太久了八!
结束
本文是在自己学习vue时遇到promise这个知识点时做的一个小记录;