众所周知,js 是一门单线程的非阻塞的脚本语言。
单线程:只有一个调用栈,同一时刻只能干一件事,代码是一段一段执行的。
调用栈:是一个数据结构,记录我们程序运行到哪一个阶段了,如果调用了函数就进栈,如果函数返回结果,就出栈(进栈出栈)。
非阻塞:代码需要进行一项异步任务的时候,主线程会挂起这个任务,然后在异步任务返回结果的时候,再根据一段的规则去执行相应的回调。
为什么是单线程的?
这是因为 js 创立之初的目的就在于与浏览器交互,而浏览器要大量操作 dom,试想一下,如果同时对某个 dom 节点进行修改和删除的操作,那会发生什么呢?所以决定了 js 只能是单线程的。
为什么非阻塞呢?
我们在页面中通常会发大量的请求,获取后端的数据去渲染页面。因为浏览器是单线程的,试想一下,当我们发出异步请求的时候,阻塞了,后面的代码都不执行了,那页面可能出现长时间白屏,极度影响用户体验。
这里,我们只谈论 Google 的 js 引擎---V8 引擎(nodeJS 也是 v8 引擎)。
主要是由两部分组成:
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,然后看到微任务,执行微任务。
1.你知道我这辈子最爱最放不下的人是谁吗?就是这句话的第一个字。 2.你的过去...
在《【AmazeUI】模态框》( 点击打开链接 )一文中,已经介绍过在AmazeUI这个手...
词法环境(Lexical Environment) 官方定义 官方 ES2020 这样定义词法环境(Lexi...
我们做完一个网页,通常代码里有空链接,挨个挨个去找,再删,好像太麻烦了,有...
我们将使用 CSS3 动画过渡来创建简单但引人入胜的链接悬停效果,将鼠标悬停在链...
看示例吧: style type="text/css" body{ margin:0; padding:30px 0 0 0; } div#m...
css学习笔记一 style type="text/css"!-- * a,table,div{font-size:12px;} --/st...
表示临时响应并需要请求者继续执行操作的状态代码。 代码 说明 100 (继续) 请...
为什么要给网站添加动态横幅?一方面,是图片不容易表现出出动画的效果,另一方...
本文主要介绍了AmazeUI 网格的实现示例,分享给大家,也给自己留个笔记。具体如...