event loop终极指南

在遇到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

结论:

  1. 微任务中,嵌套promise和连续then的优先级要高于return new Promise()和return Promise.resolve();(这是bug,chrome中是3次,node中利用了这个bug做了优化,是2次。。。我也不知道这个bug为什么有,估计得拔一下node的issure.)这是微任务之间的顺序。

  2. 微任务之间第一级总是相等。之后的嵌套子微任务的执行按照快3个的规律执行。

  3. 微任务总是优先于宏任务去执行

下面是如何判断js中的宏任务的顺序问题:
4. 每个script标签自打进入执行就是1个宏任务,可以理解为顶层宏任务
5. 宏任务总是让微任务优先执行。(微任务直接被插入当前代码执行队列尾部,而宏任务总是新开辟一个队列)。
6. 宏任务开辟任务队列的顺序拥有严格的限制,宏任务总是在扫描完成同步代码之后按照从上到下的顺序开辟任务队列。(宏任务从某种意义上来所是一种同步这个原理其实特别好理解,因为宏任务总是需要时间,而js无法精确知道事件的前提下就采用了回调的方式。只要有回调,那么回调的调用者总是在最外层被先执行,此时里面的回调函数就作为宏任务被开辟到了新的任务队列。)。

如何解决event loop中的顺序问题

使用块匹配算法
算法初衷:设计该算法的目的在于如何解决同步代码块中的顺序问题。
算法的核心思路在于对同步代码块加锁(加注释).
还记得event loop的执行顺序:

  1. 执行所有同步代码,执行过程中遇到宏任务将其开辟一条新的队列
  2. 执行所有的微任务。执行过程中注意微任务之间的并行问题;如果遇到子宏任务则开辟一条新的队列。
  3. 按照锁的顺序,依次解开锁去执行宏任务。重复执行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);
        });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值