1、js 单线程,并非指 js 只有一个线程,而是同一时刻只能有一个线程在工作。
2、js 中,主线程之外,还有其余线程,比方事件循环线程,定时器触发线程,http 异步线程,浏览器事件线程。
3、在 js 主线程中,分为两个子线程,js 引擎线程,GUI 渲染线程。这两个线程是互斥的,同一时刻只能执行一个,要么执行 js,要么渲染 html
4、工作队列中,分为宏工作微工作。每次执行工作队列时,先执行微工作,再执行宏工作。
5、通常宏工作指 setTimeout,setInterval,XMLHttprequest,fetch 等回调。微工作指 Promise,MutationObserver 等回调。
6、如果在 定时器触发线程、http 异步线程、浏览器事件线程中,没有回调,则不会放入队列中。
7、事件循环线程,必须期待主线程中的同步代码执行完结,才会去工作队列再取一个 工作放入 主线程中执行。
//index.js
console.log('a');
Promise.resolve().then(() => {console.log('b');
});
setTimeout(() => {console.log('c');
}, 0);
setTimeout(() => {console.log('d');
Promise.resolve().then(() => {console.log('e');
});
}, 0);
console.log('f');
上面是上述代码的执行逻辑:
- 遇到 console.log(‘a’); 同步代码,执行,输入 a;
- 遇到 Promise,异步代码,放入工作队列。又因为是 promise 的回调,属于微工作。标记微工作 1
- 遇到 setTimeout 执行,放入定制器触发线程中,定时器触发线程中保护何时倒计时完结,并将回调放入工作队列。又因为 setTimeout 的回调属于宏工作。标记为宏工作 1
- 又遇到 setTimeout 执行,放入定制器触发线程中,将回调放入工作队列。因为 setTimeout 的回调属于宏工作。标记为宏工作 2
- 遇到 console.log(‘f’); 同步代码,执行,输入 f
此时主线程中的同步代码曾经齐全执行,控制台输入 a,f。主线程是空的。此时事件循环线程发现,工作队列有货色,别离是微工作 1,宏工作 1,宏工作 2.
- 依照先执行微工作,再执行宏工作程序,先将微工作 1,即 () => { console.log(‘b’); } 放入主线程中由 js 执行。输入 b,
- 此时主线程执行完,又空了,此时工作队列还有宏工作 1,宏工作 2。因为宏工作 1 先放入的,依照队列的先进先出程序。先将宏工作 1 放入主线程。即 () => { console.log(‘c’); },输入 c,
- 再判断队列中是否有微工作,如果有,则全副执行。如果没有,就继续执行宏工作 2.
-
将宏工作 2 放入主线程,即
() => {console.log('d'); Promise.resolve().then(() => {console.log('e'); }); }
输入 d,遇到 promise,异步代码,放入微工作队列。标记为微工作 2。此时主线程又空了。
- 此时工作队列只有微工作 2,没有其余的宏工作和微工作。最初再执行微工作 2。即 () => { console.log(‘e’); },输入 e
总结下: 最初输入后果为 a f b c d e
留神:
1、下面的 setTimeout(()=>{}); 属于同步代码,会执行,如果 let timer = setTimeout(()=>{}); 你会发现 timer 有值,是个数字。但也仅仅是执行 setTimeout 后将援用返回,剩下的倒计时和回调。都在定时器触发线程中保护。
2、同样,下面的 Promise.resolve() 也属于同步代码,let p = Promise.resolve() . 会发现 p 有值,是个 Promise 对象,但也仅仅是执行 Promise.resolve() 后将援用返回,剩下的 then 中的回调。都在微工作队列中保护
以上是本人通过浏览其余文章,加上本人了解和本地调试之后的 一点点感想。如有问题,还望斧正。