js异步机制

3次阅读

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

前言

js 引擎不是独立运行的,它运行在宿主环境中,这个环境可以是浏览器、可以是服务器,或者其他硬件设施。所以在浏览器的帮助下,js 作为一种单线程语言,可以实现异步操作。
浏览器内核是多线程的,几个常驻的线程:渲染引擎线程、js 引擎线程、定时触发器线程、事件触发线程、异步 http 请求线程。

并发模型
https://developer.mozilla.org…

左边的栈存储的是同步任务。右边的堆用来存储声明的变量、对象。下面的队列就是任务队列,一旦某个异步任务有了响应就会被推入队列中。每个异步任务都和一个回调函数相关联。
一个 js 程序的单线程用来执行栈中的同步任务,当所有同步任务执行完毕后,栈被清空,然后读取任务队列中的一个待处理任务,并把相关回调函数压入栈中,单线程开始执行新的同步任务,执行完毕。
单线程从任务队列中读取任务是不断循环的,每次栈被清空后,都会在任务队列中读取新的任务,如果没有新的任务,就会等待,直到有新的任务,这就叫任务循环或者事件循环

事件循环 (Event Loop)
事件循环的大致流程如下:

主线程执行所有同步任务,形成一个执行栈(并发模型中的 stack)。
主线程执行同步任务的同时,子线程执行异步任务,并将相应的结果(事件)放入任务队列。
一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,把任务队列中事件相应的回调函数压入栈内开始执行。
执行回调后,栈空,继续重复第三步,形成一个循环。

Microtask 与 Macrotask(task)
Microtask 和 Macrotask(task) 是异步任务的两个分类。
macrotasks: setTimeout, setInterval, setImmediate, I/O, UI renderingmicrotasks: process.nextTick, Promises, Object.observe(废弃), MutationObserver
事件循环每次只会入栈一个 macrotask 任务,主线程会先检查 microtasks 队列并完成里面的所有任务后再执行 macrotask(task)。
举个栗子:
console.log(‘script start’);
setTimeout(function() {
console.log(‘setTimeout1’);
})
}, 0);
setTimeout(function() {
console.log(‘setTimeout2’);
}, 0);
Promise.resolve().then(function() {
console.log(‘promise1’);
}).then(function() {
console.log(‘promise2’);
});
console.log(‘script end’);
结果是:
“script start”
“script end”
“promise1”
“promise2”
“setTimeout1”
“setTimeout2”
可以看到,macrotask 是在 microtask 全部执行后才执行的。
再举一个栗子:
console.log(‘script start’);
setTimeout(function() {
console.log(‘setTimout1’);
setTimeout(function() {
console.log(‘setTimout2’);
}, 0);
Promise.resolve().then(function() {
console.log(‘promise3’);
})
}, 0);
Promise.resolve().then(function() {
console.log(‘promise1’);
}).then(function() {
console.log(‘promise2’);
});
console.log(‘script end’);
结果是:
“script start”
“script end”
“promise1”
“promise2”
“setTimout1”
“promise3”
“setTimout2”
可以发现,在执行完 setTimeout1 之后没有继续执行 setTimeout2, 而是去执行了 promise3,这也验证了:每执行一个 macrotask 任务之前都会执行完当前的 microtask 队列。
注:代码示例参考链接:https://v.youku.com/v_show/id…

正文完
 0