说说事件循环event loop(day2)

149 阅读4分钟

1)定义

首先, js 是单线程的 ,主要的任务是处理用户的交互,而用户的交互无非就是响应 DOM 的增删改,那如何处理事件响应呢?

浏览器的各种 Web API 会为异步的代码提供了一个单独的运行空间,当异步的代码运行完毕以后,会将代码中的回调送入到 Task Queue(任务队列)中去,等到调用栈空时,再将队列中的回调函数压入调用中执行,等到栈空以及任务队列也为空时,调用栈仍然会不断检测任务队列中是否有代码需要执行,这一过程就是完整的 Event loop 了。

同时需要注意的是,js 引擎在执行过程中有优先级之分, js 引擎在一次事件循环中, 会先执行 js 线程的主任务,然后会去查找是否有微任务 microtask(promise),如果有那就优先执行微任务,如果没有,再去查找宏任务 macrotask(setTimeout、setInterval) 进行执行。

事件循环与任务队列

2)执行栈

在代码运行过程中,主线程([JavaScript]引擎线程)中有一个执行栈,执行栈中,又存在两个东西,一个是堆,一个是栈,栈里面则是当前要执行的代码

  • : 一种存放复杂或者说引用类型的内存区域

  • 动态分配的内存,大小不定,不会自动释放,存放引用类型,包含引用类型的变量,实际上保存的不是变量本身,而是指向该对象的指针(地址)。

  • :一种存放数据的内存区域,当前要执行的代码

  • 先进先出,后进后出,一个有序的列表
    流程图:

    在这里插入图片描述

3)同步和异步

  • 同步任务在执行栈中执行,而异步任务执行完毕后进入任务队列

  • 当前所有同步任务执行完毕后,才会进入人物队列中执行等待中的异步任务

  • 流程图:

    在这里插入图片描述
    1. 同步任务由JavaScript主线程次序执行
    2. 异步任务委托给宿主环境执行
    3. 已完成的异步任务对应的回调函数,会被加入到任务队列中
    4. JavaScript主线程的执行栈被清空后,会读取任务队列中的回调函数,次序执行
    5. JavaScript主线程不断的重复上面的第4步

4) 常用的宏任务和微任务

1)常用的宏任务和微任务有:

名称举例(常用)
宏任务script、setTimeout 、setInterval 、setImmediate、I/O、UI Rendering
微任务process.nextTick()、Promise

上诉的 setTimeout 和 setInterval 等都是任务源,真正进入任务队列的是他们分发的任务。

2)优先级

setTimeout = setInterval 一个队列 setTimeout > setImmediate process.nextTick > Promise

for(const macroTask of macroTaskQueue){ // 2.再执行宏任务 handleMacroTask(); for(const microTask of microTaskQueue){ // 1.先执行微任务 handleMicroTask(); } } 在这里插入图片描述

  • 优先级: 微任务 => DOM渲染 => 宏任务
  • 每一个宏任务执行完之后,都会检查是否存在待执行的微任务,如果有,则执行完所有微任务,再继续执行下一个宏任务
  • 你可以理解为宏任务和微任务都是任务队列,只是用来存放东西的队列。
  • 然后呢主线程可以执行代码,渲染UI之类的。主线程空闲的时候就会去轮询看看宏任务队列有没有要执行的任务,没有就过,有就取出来执行。
  • 宏任务执行完会去看微任务队列。没有就过,有就取出来执行。

(3)setTimeout(fn,0)多久才执行,Event loop?

setTimeout 按照顺序放到队列里面,然后等待函数调用栈清空之后才开始执行,而这些操作进入队列的顺序,则由设定的延迟时间来决定。

接下来看题解

    setTimeout(() => {
            console.log('setTimeout')	
    },0); 


    Promise.resolve().then(() => {
            console.log('promise');
                    Promise.resolve().then(() => {
                            console.log('promise2');
            })
    })

    console.log('main');`
    **log(‘main’) log(‘promise’) log(‘promise2’) log(‘setTimeout’);**

    `setTimeout(() => {
            Promise.resolve().then(() => {
                    console.log('promise')
            })
    },0);

    Promise.resolve().then(() => {
            setTimeout(() => {
                    console.log('setTimeout')
            },0)
    });

    console.log('main');`
    **log(‘main’) log(‘promise’) log(‘setTimeout’);**


    `async function async1 () {
      console.log('async1 start');
      await async2();
      console.log('async end')
    }

    async function async2 () {
      console.log('async2')
    }

    console.log('script start')

    setTimeout(() => {
      console.log('serTimeout')
    }, 0)

    async1()

    new Promise((function (resolve) {
      console.log('promise1')
      resolve()
    })).then(function () {
      console.log('promise2')
    }).then(function () {
      console.log('promise3')
    }).then(function () {
      console.log('promise4')
    })
    console.log('script end')`

    // 'script start'
    // 'async1 start'
    // 'async2'
    // 'promise1'
    // 'script end'
    // 'async1 end'
    // 'promise2'  promise3  promise4
    // "serTimeout"
    // 1. 创建函数async1 创建函数async2
    // 2. 执行主线程 打印 script start
    // 3. 遇到setTime 放到宏任务
    // 4. 执行async1 函数 打印 async1 start执行async2函数打印async2 放入微任务
    // 5. 继续执行 new Promise是立即执行 打印peimise1 resolve是异步的 放到微任务
    // 6. 往下执行 打印 script end 
    // 7. 主线程执行完毕 看看微任务(拿出来一个执行一个就是event loop) 先打印 async end 然后再看看再执行 promise2
    // 8. 微任务执行完了执行宏任务 打印 settimeout