众所周知,js 是一门单线程的非阻塞的脚本语言。javascript
单线程:只有一个调用栈,同一时刻只能干一件事,代码是一段一段执行的。java
调用栈:是一个数据结构,记录咱们程序运行到哪个阶段了,若是调用了函数就进栈,若是函数返回结果,就出栈(进栈出栈)。node
非阻塞:代码须要进行一项异步任务的时候,主线程会挂起这个任务,而后在异步任务返回结果的时候,再根据一段的规则去执行相应的回调。web
为何是单线程的?
这是由于 js 创立之初的目的就在于与浏览器交互,而浏览器要大量操做 dom,试想一下,若是同时对某个 dom 节点进行修改和删除的操做,那会发生什么呢?因此决定了 js 只能是单线程的。ajax
为何非阻塞呢?
咱们在页面中一般会发大量的请求,获取后端的数据去渲染页面。由于浏览器是单线程的,试想一下,当咱们发出异步请求的时候,阻塞了,后面的代码都不执行了,那页面可能出现长时间白屏,极度影响用户体验。后端
这里,咱们只谈论 Google 的 js 引擎---V8 引擎(nodeJS 也是 v8 引擎)。api
主要是由两部分组成:promise
stack
里执行callback queue
里。stack
中的全部任务都执行完毕,主线程处于闲置状态时,主线程会去查找callback queue
是否有任务。若是有,那么主线程会从中取出回调(此处区分宏任务与微任务)放入stack
中,而后执行其中的同步代码...,如此反复。console.log(1); setTimeout(function a() { console.log(2); }, 0); new Promise(function (resolve, reject) { console.log(5); resolve(); }).then(function () { console.log(6); }); new Promise(function (resolve, reject) { resolve(); }).then(function () { console.log(7); }); console.log(3);
结果:1,5,3,6,2浏览器
全部
微任务队列中的事件。因此,先打印 6,再打印 7,在打印 2.let promiseGlobal = new Promise(function (resolve) { console.log(1); resolve("2"); }); console.log(3); promiseGlobal.then(function (data) { console.log(data); let setTimeoutInner = setTimeout(function (_) { console.log(4); }, 1000); let promiseInner = new Promise(function (resolve) { console.log(5); resolve(6); }).then(function (data) { console.log(data); }); }); let setTimeoutGlobal = setTimeout(function (_) { console.log(7); let promiseInGlobalTimeout = new Promise(function (resolve) { console.log(8); resolve(9); }).then(function (data) { console.log(data); }); }, 1000);
执行顺序是 1,3,2,5,6,间隔一秒,7,8,9,4网络
解答以下:
⚠️易错点: 之因此把这道题拿出来说,是由于这道题涉及到屡次事件循环,不少同窗容易搞混的点。
全部
微任务队列中的事件,再去宏任务队列中取出一个
事件以前
执行1.主进程
2.第三方插件进程
3.GPU 进程
4.渲染进程,就是咱们说的浏览器内核(最重要
)
1.JS 引擎线程
2.GUI 渲染线程
3.http 请求线程
4.事件处理线程(鼠标点击、ajax 等)
5.定时器触发线程
互斥
的?document.body.style = "background:black"; document.body.style = "background:red"; document.body.style = "background:blue"; document.body.style = "background:grey";
结果:背景直接变成灰色
分析:Call Stack 清空的时候,执行,执行到了 document.body.style = 'background:grey';这时,前面的代码都被覆盖了,此时 dom 渲染,背景色是灰色
document.body.style = "background:blue"; console.log(1); Promise.resolve().then(function () { console.log(2); document.body.style = "background:black"; }); console.log(3);
结果:背景直接变成黑色
分析:document.body.style = 'background:blue'是同步代码,document.body.style = 'background:black'是微任务,此时微任务执行完,才会进行 dom 渲染,因此背景色是黑色
document.body.style = "background:blue"; setTimeout(function () { document.body.style = "background:black"; }, 0);
结果:背景先一闪而过蓝色,而后变成黑色
分析:document.body.style = 'background:blue';是同步代码,document.body.style = 'background:black'是宏任务,因此 dom 在同步代码执行完,宏任务执行以前会渲染一次。而后宏任务执行完又会渲染一次。2 次渲染,因此才会呈现背景先一闪而过蓝色,而后变成黑色,这种效果。
1.先把Call Stack清空 2.而后执行当前的微任务 3.接下来DOM渲染 微任务在dom渲染`以前`执行,宏任务在dom渲染`以后`执行。
⚠️ 注意:如下内容 node 的版本大于等于 11.0.0
解释:
优先
于其余 microtask 执行。setTimeout(funciton(){console.log(1)}); setImmediate(function(){console.log(2)}); process.nextTick(function(){console.log(3)}); Promise.resolve().then(function(){console.log(4)}); (function() {console.log(5)})();
打印结果:5,3,4,1,2
清空
了,该执行的回调函数都执行了,事件循环才会进入下一个
阶段。由于执行完2个定时器,回调都进入宏任务队列了。而后开始事件循环,由于宏任务是一个个执行的,因此先把第一个定时器的回调放入调用栈中,执行完time1,把微任务放入微任务队列中。
这是调用栈清空,又开始事件循环,这时候有微任务promise1,和第二个宏任务。由于微任务在宏任务以前执行,因此先执行promise1,
这是调用栈又清空,又开始事件循环。执行第二个宏任务,打印,time2,promise2
由于已经在timer阶段了,因此。先执行完time阶段,time1,time2,而后看到微任务,执行微任务。