本人前端小白一枚,只是整理一下自己的学习过程,第一次在掘金发表自己的文章,有问题欢迎大家及时指出
之前在年初,由于疫情原因,找工作耽搁了一阵,后来通过几次视频面试,发现了一道经常被问到的代码执行顺序的问题,其中少不了对promise的说明。这里我们来一起探讨一下
A C D M Q E F G I K J B L N P H O
Promise
什么是Promise呢,第一次用Promise是在公司项目中,为了实现异步嵌套的一个需求(具体需求记不太清了),在了解Promise之前,需要了解javascript的工作机制以及特点
javascript的单线程特性
是的,javascript是单线程语言,原因很简单:在前端开发的初期,前端项目主要的运行环境是浏览器,作为浏览器端的脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。所以导致了javascript是单线程的特性。既然是单线程语言,那么在任务执行时就需要对其进行排序了,只能是前一个任务执行完毕后,才会执行下一个任务。
但是,当前一个任务执行时间过长,就会造成浏览器端的卡顿,因为单线程的缘故,只能在前一个任务结束后再执行后一个任务,所以基于这个问题,javascript产生了一个任务队列的概念
任务队列
任务队列顾名思义,就是对任务进行排序,在javascript中,所有任务可以分为两种:同步任务和异步任务。
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务指的是,不进入主线程、而进入任务队列的任务
总结一下,任务的执行顺序是在主线程中进行同步任务,异步任务进入到任务队列中,当主线程中的同步任务执行完毕后,会在任务队列中寻找未执行的异步任务,并根据任务队列中的顺序依次传递到主线程中进行执行,循环上述操作,这就是常说的eventloop
宏任务和微任务
在了解了JavaScript任务执行顺序和任务队列后,又衍生出了宏任务和微任务的概念
这里举个例子:
银行每天都会有很多客户去办理各种业务,每一个客户想要办理的业务就是一个宏任务,当这个客户想要办理的业务结束了,突然有新的业务想要办理,例如:客户A取款后,又想要办一张信用卡,按照银行的业务办理流程,不会让客户A去重新排号,而是直接在当前办理业务中继续执行办理信用卡的业务,那么,办理信用卡的这个新增的业务就是微任务。
通过例子可以看出,在当前宏任务中,若有微任务存在,会先执行当前宏任务的微任务,当所有微任务执行完毕后,才会执行下一个宏任务
明确了这一点之后,可以看一下下面的一道面试题:
setTimeout(() => console.log(1))
new Promise(resolve => {
resolve()
console.log(2)
}).then(() => {
console.log(3)
})
console.log(4)
输出结果为:2,4,1,3
分析一下这个代码:
首先,分析里面的代码,从上到下的主流程中,setTimeout为第一个宏任务,内部的匿名函数会放到任务队列中,随后走到new Promise中,执行内部的resolve和console.log(2),Promise.then()的方法会当做当前主流程的微任务放入微任务的队列中,随后代码继续执行,输出console.log(4)
主线程执行完了,首先先去微任务队列中寻找未执行的代码,找到了Promise.then方法,执行输出了console.log(3),微任务队列清空后,当前宏任务执行完毕,寻找下一个宏任务setTimeout,输出console.log(1)
看懂了么??-_-///
翻译成大白话解释一下:
整个主流程相当于银行柜员在处理业务,setTimeout的操作相当于去银行取号,取号的过程中,银行柜员同时在处理业务,new Promise为客户A(主流程)需要办理的业务,一共办理了console.log(2)和console.log(4)两个业务,在业务办理完成后,客户A临时添加了console.log(3)的业务,也就是Promise.then这个微任务,按照银行柜员的工作流程,不需要重新排号,所以直接处理了then这个业务,结束之后,客户A(主流程)没有了临时业务需要办理,这个客户的业务全部办理完成,开始处理下一个客户B(下一个宏任务)的业务,setTimeout开始执行,所以输出的结果为2,4,1,3
是不是好理解多了
通过这段代码可以继续拓展一下
setTimeout(() => console.log(1))
new Promise(resolve => {
resolve()
console.log(2)
}).then(() => {
new Promise(resolve => {
resolve()
console.log(3)
}).then(() => {
console.log(4)
})
console.log(5)
})
console.log(6)
输出结果为: 2,6,3,5,4,1
简单解释一下,微任务then中添加了额外的微任务,当前微任务未执行完毕,会继续执行,换句话说,在当前微任务中添加了额外的微任务,当前主线程会将所有微任务全部执行完毕,才会执行setTimeout的宏任务
以上就是我对代码执行顺序的理解,个人见解,希望大家指出其中的不足,欢迎讨论和批评,最后留一个面试中被问过的问题,希望大家一起研究一下(答案放在引言上了……)
new Promise((resolve, reject) => {
console.log("A");
setTimeout(() => {
console.log("B");
}, 0);
console.log("C");
resolve();
console.log("D");
})
.then(() => {
console.log("E");
new Promise((resolve, reject) => {
console.log("F");
resolve();
console.log("G");
})
.then(() => {
setTimeout(() => {
console.log("H");
}, 0);
console.log("I");
})
.then(() => {
console.log("J");
});
})
.then(() => {
console.log("K");
});
setTimeout(() => {
console.log("L");
}, 0);
new Promise((resolve, reject) => {
console.log("M");
resolve();
}).then(() => {
setTimeout(() => {
new Promise((resolve, reject) => {
console.log("N");
resolve();
})
.then(() => {
setTimeout(() => {
console.log("O");
}, 0);
})
.then(() => {
console.log("P");
});
}, 0);
});
console.log("Q");