在遇到event loop时,不需要特别关注其原理,而要关于它的顺序问题。
js通过事件来解决单线程中的异步问题,而单线程要想异步,必须通过事件来解决。
promise的作用是为了保证js单线程中的异步与异步之间(promise之间)的顺序调用问题。
微任务和宏任务之间特指的区别是微任务执行所需毫秒无限接近于零
。宏任务要么耗时,要么不确定到来的时间
。
顺序问题有两种
,一种是微任务
,一种是宏任务
。所以下面给出针对这两种的区分方法:
下面是如何判断js中的微任务的顺序问题:
js奇葩的顺序问题:
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log(1);
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log(2);
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log(3);
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log(4);
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log(5);
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log(6);
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log(7);
new Promise(function (
resolve) {
resolve();
}).then(function () {
console.log(8);
new Promise(
function (
resolve
) {
resolve
();
}).then(
function () {
console
.log(
9
);
})
})
})
});
})
})
});
});
});
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log(11);
}).then(function () {
console.log(22);
}).then(function () {
console.log(33);
}).then(function () {
console.log(44);
}).then(function () {
console.log(55);
}).then(function () {
console.log(66);
}).then(function () {
console.log(77);
}).then(function () {
console.log(88);
}).then(function () {
console.log(99);
});
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log(111);
return new Promise(function (resolve) {
resolve();
});
}).then(function () {
console.log(222);
return new Promise(function (resolve) {
resolve();
});
}).then(function () {
console.log(333);
return new Promise(function (resolve) {
resolve();
});
}).then(function () {
console.log(444);
return new Promise(function (resolve) {
resolve();
});
}).then(function () {
console.log(555);
return new Promise(function (resolve) {
resolve();
});
}).then(function () {
console.log(666);
return new Promise(function (resolve) {
resolve();
});
}).then(function () {
console.log(777);
return new Promise(function (resolve) {
resolve();
});
}).then(function () {
console.log(888);
return new Promise(function (resolve) {
resolve();
});
}).then(function () {
console.log(999);
})
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log(1111);
return Promise.resolve();
}).then(function () {
console.log(2222);
return Promise.resolve();
}).then(function () {
console.log(3333);
return Promise.resolve();
}).then(function () {
console.log(4444);
return Promise.resolve();
}).then(function () {
console.log(5555);
return Promise.resolve();
}).then(function () {
console.log(6666);
return Promise.resolve();
}).then(function () {
console.log(7777);
return Promise.resolve();
}).then(function () {
console.log(8888);
return Promise.resolve();
}).then(function () {
console.log(9999);
return Promise.resolve();
})
这段代码的执行顺序是:
1
11
111
1111
2
22
3
33
4
44
222
2222
5
55
6
66
7
77
333
3333
8
88
9
99
444
4444
555
5555
666
6666
777
7777
888
8888
999
9999
结论:
-
微任务中,嵌套promise和连续then的优先级要高于return new Promise()和return Promise.resolve();(
这是bug,chrome中是3次,node中利用了这个bug做了优化,是2次。。。我也不知道这个bug为什么有,估计得拔一下node的issure.
)这是微任务之间的顺序。 -
微任务之间第一级总是相等。之后的嵌套子微任务的执行按照
快3个
的规律执行。 -
微任务总是优先于宏任务去
执行
。
下面是如何判断js中的宏任务的顺序问题:
4. 每个script标签自打进入执行就是1个宏任务,可以理解为顶层宏任务
。
5. 宏任务总是让微任务优先执行。(微任务直接被插入当前代码执行队列尾部,而宏任务总是新开辟一个队列)。
6. 宏任务开辟任务队列的顺序拥有严格的限制,宏任务总是在扫描完成同步代码之后
按照从上到下
的顺序开辟任务队列。(宏任务从某种意义上来所是一种同步
。这个原理其实特别好理解,因为宏任务总是需要时间,而js无法精确知道事件的前提下就采用了回调的方式。只要有回调,那么回调的调用者总是在最外层被先执行,此时里面的回调函数就作为宏任务被开辟到了新的任务队列。
)。
如何解决event loop中的顺序问题
使用块匹配算法
。
算法初衷:设计该算法的目的在于如何解决同步代码块中的顺序问题。
算法的核心思路在于对同步代码块加锁
(加注释).
还记得event loop的执行顺序:
- 执行所有同步代码,执行过程中
遇到宏任务将其开辟一条新的队列
- 执行所有的微任务。执行过程中注意微任务之间的并行问题;如果遇到子宏任务则开辟一条新的队列。
- 按照锁的顺序,依次解开锁去执行宏任务。重复执行1。
这个算法最核心
解决的问题就是想要将上文中红色标记语句用代码的注释注释起来,省的写顺序时考虑。
当所有顺序、微任务代码执行完后再依次解开注释去考虑。
例子
console.log(1); //1
new Promise(function (res, rej) {
console.log(2); //2
res();
})
.then(function () {
console.log(3); //5
Promise.resolve().then(function () {
console.log(5); //6
// B5
setTimeout(function () {
console.log(6); //15
Promise.resolve().then(function () {
console.log(7); //16
});
// B7
setTimeout(function () {
console.log(8); //18
}, 0);
}, 0);
});
})
.then(function () {
console.log(4); //7
});
// B1
setTimeout(function () {
console.log(9); //8
new Promise(function (res) {
res();
console.log(10); //9
}).then(function () {
console.log(11); //10
});
});
Promise.resolve().then(function () {
// B4
setTimeout(function () {
Promise.resolve().then(function () {
console.log(12); //14
});
console.log(13); //13
}, 0);
});
// B2
setTimeout(function () {
// B6
setTimeout(function () {
// B8
setTimeout(function () {
Promise.resolve().then(function () {
console.log(14); //20
});
console.log(15); //19
}, 0);
console.log(16); //17
}, 0);
console.log(17); //11
}, 0);
console.log(18); //3
new Promise(function (res) {
console.log(19); //4
// B3
setTimeout(function () {
console.log(20); //12
}, 0);
});