js异步机制

前言

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…

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理