题目背景
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');
复制代码
题目的本质,是考察setTimeout
、promise
、async await
的实现顺序及JS的时间循环方面的相关问题。
结果为: script start async1 start async2 promise1 script end async1 end promise2 settimeout
这里涉及到同步异步Microtasks
和Macrotasks
。 Microtasks
优先级别高于Macrotasks
其中Microtasks
、Macrotasks
分别是:
microtasks:
- process.nextTick
- promise
- Object.observe
- MutationObserver
macrotasks:
- setTimeout
- setInterval
- setImmediate
- I/O
- UI渲染
- 一个事件循环(event loop)会有一个或多个任务队列(task queue)
- task queue 就是 macrotask queue
- 每一个 event loop 都有一个 microtask queue
- task queue == macrotask queue != microtask queue
- 一个任务 task 可以放入 macrotask queue 也可以放入 microtask queue 中
因此事件循环的顺序,决定了JavaScript代码的执行顺序。它从script(整体代码)开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的micro-task。当所有可执行的micro-task执行完毕之后。循环再次从macro-task开始,找到其中一个任务队列执行完毕,然后再执行所有的micro-task,这样一直循环下去。
定时器是怎么实现定时的?为什么会出现不定时的情况?
首先,要明确的一点:javascript是以单线程的方式运行的。JavaScript的主要用途是与用户互动,以及操作DOM。若以多线程的方式,则可能出现冲突。假设有两个线程同时操作一个DOM元素,线程1要求浏览器删除DOM,而线程2却要求修改DOM样式,这时浏览器就无法决定采用哪个线程的操作。当然,我们可以为浏览器引入“锁”的机制来解决这些冲突,但大大提高复杂性,所以 JavaScript从诞生开始就选择了单线程执行。在某一时刻内只能执行特定的一个任务,并且会阻塞其它任务执行。 但是JavaScript 有个基于“Event Loop”并发的模型(不是并行)。前者是逻辑上的同时发生,而后者是物理上的同时发生。所以,单核处理器也能实现并发。 上图说明一下并发和并行:
小tips:
console.log(1);
setTimeout(function(){
console.log(2);
Promise.resolve(1).then(function(){
console.log('ok')
})
})
setTimeout(function(){
console.log(3)
})
复制代码
分析:先默认走栈,输出1。此时并没有微任务,所以微任务不会执行。先走第一个setTimeout,输出2,同时将微任务放到队列中,执行微任务,输出ok,微任务执行完,再走宏任务,输出3。
Node.js的Event Loop
V8引擎解析JavaScript脚本。 解析后的代码,调用Node API。 libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。 V8引擎再将结果返回给用户。