JS事件循环机制(微任务和宏任务)

1  event loop 是什么

event loop(事件循环/事件轮询)

  • JS是单线程运行的
  • 异步要基于回调来实现
  • event loop 就是异步回调的实现原理

JS如何执行?

  • 从前到后,一行一行执行
  • 如果某一行执行报错,则停止下面代码的执行
  • 先把同步代码执行完,再执行异步

2  event loop 运行过程

过程1:

  • 同步代码,一行一行放在Call Stack 执行
  • 遇到异步,会先 “记录” 下,放到 Web APIS, 等待时机(定时,网络请求等)
  • 时机到了,就移动到 Callback Queue

过程2:

  • 如 Call Stack 为空(即同步代码执行完) Event Loop开始工作
  • 轮询查找Callback Queue,如有则移动到Call Stack 执行
  • 然后继续轮询查找(永动机一样)

 

 

3  event loop和DOM事件的关系

  • JS是单线程的
  • 异步(setTimeout,ajax等)使用回调,基于event loop
  • DOM事件也使用回调,基于event loop

注意:如果是直接调用了DOM 事件,不是等待用户点击的话,会先执行

    const boxDom = document.querySelector('.box')
    console.log(1);
    boxDom.addEventListener("click", () => {
        console.log(2);
    })
    boxDom.click() //按照事件轮询机制,异步任务都会在同步任务完成之后再执行,然而结果却是 1  2  3  dom事件的回调没有等待 执行了
    console.log(3);

 

 

4  什么是宏任务和微任务

  • 宏任务:setTimeout,setInterval,Ajax,DOM事件
  • 微任务:Promise / async-await
  • 微任务执行时机比宏任务要早
    // 宏任务macroTask和微任务microTask
    console.log(100);
    setTimeout(() => {
        console.log(200);
    });
    Promise.reject().catch(() => {
        console.log(300);
    })
    Promise.resolve().then(() => {
        console.log(301);
    })
    console.log(400);

    // 结果是: 100  400  300  301  200

 

 

5  event loop和 DOM 渲染的关系

  • 再次回归一遍event loop的过程
  • JS 是单线程的,而且和DOM渲染共用一个线程
  • JS执行的时候,得留一些时机供DOM渲染

 

6  为什么微任务比宏任务更早

  • 宏任务:DOM渲染后触发,如setTimeout
  • 微任务:DOM渲染前触发,如Promise
  • 先演示现象,稍后再追究原理
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="container"></div>                                  
    <!-- DOM渲染  //执行顺序: 4 -->
</body>
<script>
    const container = document.getElementById('container')
    const p1 = "<p>一段文字1</p>"
    const p2 = "<p>一段文字2</p>"
    const p3 = "<p>一段文字3</p>"
    container.innerHTML = p1
    container.innerHTML += p2
    container.innerHTML += p3
    console.log('length', container.children.length);           //执行顺序: 1
    alert('本次call stack 结束,DOM结构已更新,但尚未触发渲染')   //执行顺序: 2     

    // 宏任务 DOM渲染之后触发
    setTimeout(() => {
        const length = container.children.length;
        alert(`macro Task 宏任务 ${length}`)                    //执行顺序: 5
    }, 2000);

    // 微任务 DOM渲染之前触发
    Promise.resolve().then(() => {
        const length = container.children.length;
        alert(`micro Task 微任务 ${length}`)                      //执行顺序: 3     
    })
</script>

</html>

 

7  微任务和宏任务的根本区别

  • 微任务是ES6语法规定的
  • 宏任务是由浏览器规定的
  • 微任务不会经过Web APIs,因为Promise不是W3C规范
  • 微任务执行时机比宏任务要早

执行顺序:

  1. 执行同步代码,Call Stack 清空
  2. 执行当前的微任务 队列(micro task queue)
  3. 尝试DOM渲染
  4. Event loop 开始启动,执行宏任务队列

8 解答异步问题

    // 第一题
    Promise.resolve().then(() => {
        console.log(1); //前面是resolve,走then里面的逻辑  打印1 ,之后默认返回成功
    }).catch(() => {
        console.log(2); //catch 不会触发
    }).then(() => {
        console.log(3); //打印出 3
    })
    // 结果: 1   3 
    // 第二题
    Promise.resolve().then(() => {
        console.log(11); // 前面是resolve  这里可打印出 1
        throw new Error('error1') //返回异常
    }).catch(() => {
        console.log(22); //catch捕获到前面的异常,默认返回成功状态   打印出 2
    }).then(() => {
        console.log(33); // 前面是成功状态,then可以捕获到 打印出3
    })
    // 结果为: 11  22  33 

 

    // 第三题
    Promise.resolve().then(() => {
        console.log(111);
        throw new Error('error1')
    }).catch(() => {
        console.log(222);
    }).catch(() => {
        console.log(333);
    })
    // 思路同上: 结果为 111  222 
  // 第四题
  // 场景题: async/await语法
    async function fn() {
            return 100   //返回resolve成功状态,100是参数传递下去
        }
        (async function () {
            const a = fn(); //a是: Promise
            const b = await fn(); //b是:100  await相当于then
            console.log(a, b);
            a.then(res => {
                console.log(res);  // 100
            })
        })()
// 第五题    
// 下面执行完毕,打印出哪些内容?
    (async function () {
        console.log('start'); // start
        const a = await 100
        console.log("a", a); // 成功状态的promise,传了个  100
        const b = await Promise.resolve(200)
        console.log('b', b); // 200
        const c = await Promise.reject(300) //await相当于成功,等待不到一个失败状态的promise
        console.log('c', c); //报错
        console.log("end"); //前面报错,后面的代码不执行
    })()
 // 第六题   
 // 综合题
    async function async1() {
        console.log('async1 start'); // 执行顺序:2
        await async2();
        // await 后面作为回调内容 ==> 微任务
        console.log('async1 end'); //执行顺序:6
    }

    async function async2() {
        console.log('async2'); //执行顺序:3
    }
    console.log('script start'); // 执行顺序:1.先执行同步任务


    setTimeout(function () { // 宏任务
        console.log('setTimeout'); //执行顺序:8.宏任务在微任务后面执行
    }, 0)

    async1()

    // 初始化Promise 传入的函数会立刻执行
    new Promise(function (resolve) {
        console.log('promise1'); //执行顺序:4
        resolve();

    }).then(() => { //微任务
        console.log('promise2'); //执行顺序:7
    })

    console.log('script end'); // 执行顺序:5.执行同步任务


    // 同步代码执行完毕(event loop -- call stack 被清空)==》 执行微任务 ==》 尝试触发DOM渲染 ==》 触发event loop 执行宏任务

 

 

9 总结

  • async/await 解决了异步回调地狱,是一个很香的语法糖
  • async/await和promise的关系

执行async函数,返回的是Promise对象 await相当于Promise的then try...catch可捕获异常,相当于Promise的catch

 

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值