javascript 单线程指的是浏览器中负责解释和执行 javascript 代码的只有一个线程,即为 js 引擎线程,但是浏览器的渲染进程是提供多个线程的,如下:
- js 引擎线程
- 事件触发线程
- 定时器触发线程
- 异步 http 请求线程
- GUI 渲染线程
事件循环和事件队列的维护是由事件触发线程控制的。
事件触发线程线程同样是由浏览器渲染引擎提供的,它会维护一个事件队列。
js 引擎遇到上文所列的异步任务后,会交个相应的线程去维护异步任务,等待某个时机,然后由事件触发线程将异步任务对应的回调函数加入到事件队列中,事件队列中的函数等待被执行。
js 引擎在执行过程中,遇到同步任务,会将任务直接压入执行栈中执行,当执行栈为空(即 js 引擎线程空闲),事件触发线程会从事件队列中取出一个任务(即异步任务的回调函数)放入执行在栈中执行。
执行完了之后,执行栈再次为空,事件触发线程会重复上一步的操作,再从事件队列中取出一个消息,这种机制就被称为事件循环(Event Loop)机制。
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
// script start
// script end
// promise1
// promise2
// timer over
所有的任务都划分到宏任务和微任务下:
macrotask: script 主代码块、setTimeout、setInterval、requestAnimationFrame、node 中的setimmediate 等。
microtask: Promise.then catch finally、MutationObserver、node 中的process.nextTick 等。
js 引擎首先执行主代码块。
执行栈每次执行的代码就是一个宏任务,包括任务队列(宏任务队列)中的。执行栈中的任务执行完毕后,js 引擎会从宏任务队列中去添加任务到执行栈中,即同样是事件循环的机制。
当在执行宏任务遇到微任务 Promise.then 时,会创建一个微任务,并加入到微任务队列中的队尾。
微任务是在宏任务执行的时候创建的,而在下一个宏任务执行之前,浏览器会对页面重新渲染(task >> render >> task(任务队列中读取))。同时,在上一个宏任务执行完成后,页面渲染之前,会执行当前微任务队列中的所有微任务
1 先执行主体代码(宏任务);
2 遇到同步代码,在执行栈中直接执行;
3 遇到 settineout 异步宏任务 ,放入宏任务任务队列中;
4 遇到promise .reslove部分执行栈直接执行;then 函数 放入微任务异步任务队列里;
5 遇到同步任务 直接执行;
6 执行栈全部清空;同步任务执行完后,开始第一个事件循环,到任务队列中检查有没有微任务;有,放入执行栈执行;
7 如此往复检查任务队列;再放入执行栈执行;再检查,再执行,叫事件循环;