javascript是单线程执行的程序,也就是它只有一条主线,所有的程序都是逐行“排队”执行,在这种状况下可能存在一些问题,比如说setTimeout、ajax期待执行的工夫较长,就会阻塞后续代码的执行,使得整个程序执行的耗时十分久,那么为了应答这样一个问题,javascript代码在执行的时候,是有几个“通道”的。

首先是调用栈,执行耗时较短的操作,耗时较长的操作先搁置到工作队列中,工作队列又分为宏工作和微工作,微工作中队列中搁置的是 promise.then、aysnc、await 这样操作,宏工作队列中搁置的是 setTimeout、ajax、onClick事件,等调用栈的工作执行实现再轮询微工作队列,微工作队列中工作执行实现之后再执行宏工作。

这里提到了栈和队列,简略说一下这两种数据结构,栈是一种后进先出的构造,只能从尾部进入,从尾部删除,拿生存中的场景来打比方,就如同自助餐的餐盘,最先放的盘子在最底下,最初放的盘子在最下面,须要把最下面的盘子一个个拿走,能力拿到最上面的盘子。

而队列,是一种先进先出的构造,从尾部进入,从头部删除,就像咱们去排队买货色,先去的同学能够先买到。

再回到事件循环机制(event loop),不阻塞主过程的程序放入调用栈中,压入栈底,执行完了就会弹出,如果是函数,那么执行完函数里所有的内容才会弹出,而阻塞主过程的程序放入工作队列中,他们须要“排队”顺次执行。

首先来个简略的例子,判断以下程序的执行程序

new Promise(resolve => {  console.log('promise');  resolve(5);}).then(value=>{  console.log('then回调', value)})function func1() {  console.log('func1');}setTimeout(() => {  console.log('setTimeout');});func1();

创立一个promise的实例就是开启了一个异步工作,传入的回调函数,也叫做excutor 函数,会立即执行,所以输出promise,应用resolve返回一个胜利的执行后果,then函数里的执行会推入到微工作队列中期待调用栈执行实现才顺次执行。

向下执行发现定义了一个func1的函数,函数此时没有被调用,则不会推入调用栈中执行。程序持续往下,发现调用setTimeout函数,将打印的setTimeout推入宏工作队列,再往下执行调用函数func1,将func1推入调用栈中,执行func1函数,此时输入fun1。

调用栈里所有的内容都执行实现,开始轮询微工作队列,输出then回调5,最初执行宏工作队列,输出setTimeout

 再来看一个简单的例子

setTimeout(function () {  console.log("set1");  new Promise(function (resolve) {    resolve();  }).then(function () {    new Promise(function (resolve) {      resolve();    }).then(function () {      console.log("then4");    });    console.log("then2");  });});new Promise(function (resolve) {  console.log("pr1");  resolve();}).then(function () {  console.log("then1");});setTimeout(function () {  console.log("set2");});console.log(2);queueMicrotask(() => {  console.log("queueMicrotask1")});new Promise(function (resolve) {  resolve();}).then(function () {  console.log("then3");});

setTimeout执行的回调函数("set1")间接被搁置到宏工作队列中期待,Promise的excutor函数立即执行,首先输出 pr1,Promise.then函数("then1")放入微工作队列中期待,上面的setTimeout执行的回调函数("set2")也被搁置到宏工作队列中,排在("set1")前面,接下来调用栈中输入2,queueMicrotask示意开启一个微工作,与Promise.then函数成果统一,("queueMicrotask1")放入微工作队列中,再往下执行,new Promise的excutor函数立即执行,then函数("then3")放到微工作队列中期待,此时调用栈出已顺次输出pr1,2。

调用栈中程序已执行完,来到微工作队列中执行微工作,顺次输入then1,queueMicrotask1,then3。

此时微工作队列中的工作也执行实现,来到宏工作队列中,输入set1,执行Promise的excutor函数,resolve即返回胜利的执行后果,then函数("then2")放入微工作中,一旦微工作队列中有工作,就不会往后执行宏工作,所以宏工作队列中的另一个setTimeout函数("set2")此时不会执行,来到微工作队列中执行("then2"),输入then2,再执行一个promise函数,("then4")被放入到微工作队列中,输入then4。

微工作队列也都执行实现,此时来到宏工作队列中,执行set2。

所以最初的输入后果为:

pr12then1queueMicrotask1then3set1then2then4set2

简略图示如下

最初一道题,加上了 async、await

先来一个论断,通过async定义的函数在调用栈中执行,await 将异步程序变成同步,所以await前面执行的程序须要等到await定义的函数执行实现才执行,须要在微工作队列中期待

async function async1 () {  console.log('async1 start')  await async2();  console.log('async1 end')}async function async2 () {  console.log('async2')}console.log('script start')setTimeout(function () {  console.log('setTimeout')}, 0)async1();new Promise (function (resolve) {  console.log('promise1')  resolve();}).then (function () {  console.log('promise2')})console.log('script end')

函数只有调用的时候才会推入调用栈中,所以最先执行的是 console.log,即输入 script start,而后setTimeout函数("setTimeout")放入宏工作队列中期待,调用async1函数,输入 async1 start,执行async2函数,输入async2,("async1 end")放入微工作队列中期待,持续向下执行Promise函数,输入 promise1,then函数中的("promise2")放入微工作队列中期待,输入 script end。

调用栈的程序都已执行结束,此时开始执行微工作队列中的程序,顺次输入 async1 end、promise2。

微工作队列中的程序也已执行实现,开始执行宏工作中的程序,输入setTimeout。

输入程序为

script startasync1 startasync2promise1script endasync1 endpromise2setTimeout

简略图示如下

判断执行程序能够记住以下几个重点

1、promise中的回调函数立即执行,then中的回调函数会推入微工作队列中,期待调用栈所有工作执行完才执行

2、async函数里的内容是放入调用栈执行的,await的下一行内容是放入微工作执行的

3、调用栈执行实现后,会一直的轮询微工作队列,即便先将宏工作推入队列,也会先执行微工作