共计 2370 个字符,预计需要花费 6 分钟才能阅读完成。
从一道题目登程
明天看到一道面试题,是对于 async/await、promise 和 setTimeout 的执行程序,题目如下:
async function async1() {
console.log('async1 start');
await async2();
console.log('asnyc1 end');
}
async function async2() {
console.log('async2');
}
console.log(‘script start’);
setTimeout(() => {
console.log('setTimeOut');
}, 0);
async1();
new Promise(function (reslove) {
console.log('promise1');
reslove();
}).then(function () {
console.log('promise2');
})
console.log(‘script end’);
复制代码
我给出的答案:
script start
async1 start
async2
asnyc1 end // x
promise1
script end
promise2
setTimeOut
复制代码
正确的答案:
script start
async1 start
async2
promise1
script end
asnyc1 end
promise2
setTimeOut
复制代码
为什么 promise1 比 asnyc1 end 先进去呢?带着这个疑难,我去理解了一下事件循环机制。
js EventLoop 事件循环机制
JavaScript 的事件分两种:
宏工作 (macro-task) 微工作 (micro-task)scriptpromise. then/catch/finally )setTimeoutprocess.nextTick(Node.js 环境)setIntervalMutaionOberver(浏览器环境)setImmediate(Node.js 环境)Object.observeIO 操作 xUI 交互事件 xpostMessagexMessageChannelx
事件的执行程序,是先执行宏工作,而后执行微工作,这个是根底,工作能够有同步工作和异步工作,同步的进入主线程,异步的进入 Event Table 并注册函数,异步事件实现后,会将回调函数放入 Event Queue 中(宏工作和微工作是不同的 Event Queue),同步工作执行实现后,会从 Event Queue 中读取事件放入主线程执行,回调函数中可能还会蕴含不同的工作,因而会循环执行上述操作。
留神:setTimeOut 并不是间接的把你的回掉函数放进上述的异步队列中去,而是在定时器的工夫到了之后,把回掉函数放到执行异步队列中去。如果此时这个队列曾经有很多工作了,那就排在他们的前面。这也就解释了为什么 setTimeOut 为什么不能精准的执行的问题了。setTimeOut 执行须要满足两个条件:
主过程必须是闲暇的状态,如果到工夫了,主过程不闲暇也不会执行你的回调函数
这个回调函数须要等到插入异步队列时后面的异步函数都执行完了,才会执行
promise、async/await
首先,new Promise 是同步的工作,会被放到主过程中去立刻执行。而.then()函数是异步工作会放到异步队列中去,那什么时候放到异步队列中去呢?当你的 promise 状态完结的时候,就会立刻放进异步队列中去了。
带 async 关键字的函数会返回一个 promise 对象,如果外面没有 await,执行起来等同于一般函数;如果没有 await,async 函数并没有很厉害是不是。
await 关键字要在 async 关键字函数的外部,await 写在里面会报错;await 如同他的语意,就是在期待,期待右侧的表达式实现。此时的 await 会让出线程,阻塞 async 内后续的代码,先去执行 async 外的代码。等里面的同步代码执行结束,才会执行外面的后续代码。就算 await 的不是 promise 对象,是一个同步函数,也会等这样操作。
流程梳理
咱们整体再梳理一下下面代码执行的流程:
整个代码片段(script)作为一个宏工作执行 console.log(‘script start’),输入 script start;
执行 setTimeout,是一个异步动作,放入宏工作异步队列中;
执行 async1(),输入 async1 start,持续向下执行;
执行 async2(),输入 async2,并返回了一个 promise 对象,await 让出了线程,把返回的 promise 退出了微工作异步队列,所以 async1()上面的代码也要期待下面实现后继续执行;
执行 new Promise,输入 promise1,而后将 resolve()放入微工作异步队列;
执行 console.log(‘script end’),输入 script end;
到此同步的代码就都执行实现了,而后去微工作异步队列里去获取工作
接下来执行 resolve(async2 返回的 promise 返回的),输入了 async1 end;
而后执行 resolve(new Promise 的),输入了 promise2;
最初执行 setTimeout,输入了 settimeout。
在第 4 步中,await 这里有一个机制,就是 await 的期待,不会阻塞内部函数的执行,而 await 期待的 如果是一个 Promise 则 Promise 外面的代码还是同步执行,如果不是 Promise,就会应用 Promise.resolve 来进行封装,这里的 async2 是一个 async 办法,外面的 打印会同步执行,而 await async2() 前面的代码 会放到微工作队列中的第一个地位,期待内部同步代码执行结束当前再执行。
所以我晓得了 script end 为什么会优先于 async1 end 输入。