JavaScript是单线程的脚本语言,也就是代码是一行一行的按顺序执行。对于异步执行代码的需求ES5是使用callback函数。
console.log("start");
setTimeout(()=>{
console.log("show after 2s");
},2000);
console.log("end")
上边代码执行结果为:
start
end
show after 2s
这是因为JS引擎会直接执行同步代码,所以会打出第一行代码的“start”,接着timeout是异步代码,会被押入栈中,JS当中的Event Loop会一直进行监听,如果主线程没有要执行的代码时会执行栈中代码,现在主线程还有代码没执行,所以先执行最后一行代码“end”。之后等待2s后,EVENT LOOP察觉到到了该执行栈中代码了,而且主线程空着,OK执行打印”show after 2S“
PS:即使延迟不是2s而是0s顺序也不会改变,因为timeout还是会押入栈中,等待主线程执行完毕再执行。
ES6针对于这种异步方案提出了新的解决方案--promises。为何引入新的方法呢?肯定是老方法--callback有弊端呗。这个弊端称之为回调地狱:
setTimeout(()=>{
console.log("第一级回调");
setTimeout(()=>{
console.log("第二级回调");
setTimeout(()=>{
console.log("第三级回调");
setTimeout(()=>{
console.log("第四级回调");
setTimeout(()=>{
console.log("第五级回调");
},2000);
},2000);
},2000);
},2000);
},2000);
可以看到如果下一个回调函数必须取决于上一个回调函数执行完毕,到了第五级代码就很难看懂了,这对debug来说就是个地狱。
Promises
就像金融一样,金融最重要的是信任,我信任你所以把钱借给你,而你也许诺我一段时间后把本金与利息付给我。我们的Promises也是一样,它向JS引擎许诺,在未来的某一时刻,我会告诉你执行的结果,成与不成都会告知于你。
const setTimer = (i) => {
let promise = new Promise((resolve,reject)=>{
try{
setTimeout(()=>{
console.log(`第{i}级回调`);
resolve(i + 1);
},2000)
}catch(err){
reject(err);
}
});
return promise;
}
上段代码我们将timeout这个回调函数包装成了promise,这个promise许诺JS引擎,2s后你将收到函数执行成功的消息,也就是resolve。promise在未完成时的状态为pending,当执行完resolve或reject函数后promise才变成完成状态,且resolve或reject的参数将会被传递,参数可以为任意类型。
所以回调地狱就改善成为:
setTimer(1)
//此catch可以捕获setTimer(1)中的Exception,并继续执行接下来的promise
//.catch(err => console.log(err);)
.then(i=>{
return setTimer(i);
})
.then(i=>{
return setTimer(i);
})
.then(i=>{
return setTimer(i);
})
.then(i=>{
return setTimer(i);
})
//若catch放在最后,则任何exception都会中断promise链,不会继续执行发生exception后的代码
;
then方法会等到promise的状态有pending转变为完成状态后才会执行then里边的代码段,并且将promise的resolve里传递的参数获取到,例如上段代码then中的i便是resolve传递过来的。可以看到回调地狱得到妥善解决。
Async/await
怎么说呢,虽然代码有了很大改善,但总觉得代码量还是太多了:
async function print(){
let i = 1;
try{
await setTimer(i++);
await setTimer(i++);
await setTimer(i++);
await setTimer(i++);
await setTimer(i++);
}catch(err){
console.log(err);
}
}
如果在函数最前面加上async关键字,就表示此函数是返回Promise的,可以把此函数想象成自动new 了一个promise并返回了,在async函数里可以添加await关键字,表明要等待await后边的promise执行完毕才可以执行下一行代码,相当于之前的then语句了。
这样改动后的代码简介易读,尤其在我们做http request的时候,会用到大量的异步代码,多使用async/await同样也会是你的运行速度变快,现在的浏览器对async/await有优化。