js运行机制及异步编程(二)

39次阅读

共计 2055 个字符,预计需要花费 6 分钟才能阅读完成。

上一篇文章主要整理了一下 js 引擎是如何工作的,这篇文章主要整理 js 的事件循环 Event loop,以及异步编程的原理
事件循环 Event loop
之前文章中有讲到 js 是单线程的,而浏览器渲染内核包含多个线程的,和 js 代码部分相关的线程有如下几个:

js 引擎线程
处理 ajax 请求的线程,
处理 DOM 事件的线程,
定时器,
读写文件的线程(Node.JS)等

因为 JS 是单线程的,这是从 JS 引擎的角度来看的,所谓的单线程就是指在 JS 引擎中负责解释和执行 JS 代码的线程只有一个:主线程。
js 分为同步任务和异步任务,同步任务都在主线程上执行,就形成一个执行栈,主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列中放置一个事件。一旦执行栈中所有的同步任务执行完毕(此时 js 引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。

JS 的执行机制就是一个主线程 + 一个任务队列。同步任务就是放在主线程上执行的任务,异步任务就是放在任务队列的任务。所有的同步任务都在主线程执行,这构成了一个执行栈,异步任务有了运行结果会在任务队列中放置一个事件,比如定时 2 秒,到 2 秒后才能放进任务队列(callback 放进任务队列,而不是 setTimeout 函数放进队列)。
事件循环(Event Loop)—— 脚本运行时,先依次运行执行栈,然后从队列中提取事件来运行任务队列中的任务,这个过程是不断重复的。所以叫事件循环(Event Loop)。
macrotask 和 microtask
上述 js 运行的机制主要说明的是 es5,而 es6 的出现及普及,又有了新的概念,promise,它的出现,进一步,JS 中分为两种任务类型:macrotask 和 microtask,在 ECMAScript 中,microtask 称为 jobs,macrotask 可称为 task。

分别很么样的场景会形成 macrotask 和 microtask 呢?

macrotask:主代码块,setTimeout,setInterval, setImmediate, I/O, UI rendering. 等(可以看到,事件队列中的每一个事件都是一个 macrotask)
microtask:process.nextTick, Promise(原生),Object.observe,MutationObserver

在 node 环境下,process.nextTick 的优先级高于 Promise,也就是可以简单理解为:在宏任务结束后会先执行微任务队列中的 nextTickQueue 部分,然后才会执行微任务中的 Promise 部分。
总结下运行机制:

执行一个宏任务(栈中没有就从事件队列中获取)
执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
当前宏任务执行完毕,开始检查渲染,然后 GUI 线程接管渲染
渲染完毕后,JS 线程继续接管,开始下一个宏任务(从事件队列中获取)

案例说明
案例 1:
setImmediate(function(){
console.log(1);
},0);
setTimeout(function(){
console.log(2);
},0);
new Promise(function(resolve){
console.log(3);
resolve();
console.log(4);
}).then(function(){
console.log(5);
});
console.log(6);
process.nextTick(function(){
console.log(7);
});
console.log(8);

根据 js 的运行原理解释上面代码的执行顺序:
第一步 script 整体代码被执行,执行过程为创建 setImmediate macro-task 创建 setTimeout macro-task 创建 micro-task Promise.then 的回调,并执行 script console.log(3); resolve(); console.log(4); 此时输出 3 和 4,虽然 resolve 调用了,执行了但是整体代码还没执行完,无法进入 Promise.then 流程。console.log(6) 输出 6process.nextTick 创建 micro-taskconsole.log(8) 输出 8 第一个过程过后,已经输出了 3 4 6 8
第二步 由于其他 micro-task 的 优先级高于 macro-task。
此时 micro-task 中有两个任务按照优先级 process.nextTick 高于 Promise。
所以先输出 7,再输出 5

第三步 micro-task 任务列表已经执行完毕,家下来执行 macro-task. 由于 setTimeout 的优先级高于 setIImmediate,所以先输出 2,再输出 1。
参考:https://blog.csdn.net/gy_u_yg… https://blog.csdn.net/m0_3775…

正文完
 0