EventLoop 事件循环机制

 

一.  JavaScript是一门单线程非阻塞的脚本语言(这是由最初的用途所决定:与浏览器交互)

  1. 单线程

       JavaScript语言的一大特点是单线程,为何是单线程呢?原因之一在于当初也是最主要的执行环境 —浏览器中,在浏览器中,我们需要执行各种各样的dom操作,试想一下,如果是多线程的,那么两个或者多个线程对同一dom操作时,比如一个向其添加事件,另一个删除了这个dom,此时就会出现问题了。为了保证不会发生类似的情况,JavaScript选择只用一个主线程来执行代码,也就是说,每次只能执行一项任务,其他任务都得按照顺序排队等待被执行,只有当前的任务执行完成之后,才会执行下一个任务。

       当然,现如今人们也意识到,单线程在保证执行顺序的同时,也限制了JavaScript的效率,因此开发出了web worker技术(这项技术号称让JavaScript成为一门多线程语言),但是使用web worker这项技术开的多线程也存在很多限制,例如:所有新线程都受主线程的完全控制,不能独立执行。这意味着这些“线程” 实际上应属于主线程的子线程。另外,这些子线程并没有执行I/O操作的权限,只能为主线程分担一些诸如计算等任务。所以严格来讲这些线程并没有完整的功能,也因此这项技术并非改变了javascript语言的单线程本质。

      2.非阻塞

        非阻塞则是当代码需要进行一项异步任务(无法立刻返回结果,需要花一定时间才能返回的任务,如I/O事件)的时候,主线程会挂起(pending)这个任务,然后在异步任务返回结果的时候再根据一定规则去执行相应的回调。非阻塞是通过事件循环机制实现的。 JS通常是非阻塞的,除了某些特殊情况,JS会停止代码执行,比如:

       1.   alert, confirm, prompt(除了Opera)

       2.   “页面上的程序正忙”的系统对话框弹出

二. 浏览器中的js事件循环机制

     1. 任务队列

        所有的任务可以分为同步任务和异步任务,同步任务,顾名思义,就是立即执行的任务,同步任务一般会直接进入到主线程中执行;而异步任务,就是异步执行的任务,比如ajax网络请求,setTimeout 定时函数等都属于异步任务,异步任务会通过任务队列( Event Queue )的机制来进行协调。 同步和异步任务分别进入不同的执行环境,同步的进入主线程,即主执行栈,异步的进入 Event Queue 。主线程内的任务执行完毕为空,会去 Event Queue 读取对应的任务,推入主线程执行。 上述过程的不断重复就是我们说的 Event Loop (事件循环)。

      如下图所示:

       同步和异步任务分别进入不同的执行环境,同步的进入主线程,即主执行栈,异步的进入任务队列。主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。 上述过程的不断重复就是我们说的 Event Loop (事件循环)。

 2.事件循环     

    在事件循环中,每进行一次循环操作称为tick,每一次 tick 的任务处理模型是比较复杂的,其关键的步骤可以总结如下:

  1. 执行栈选择最先进入队列的宏任务(通常是script整体代码),开始执行宏任务 ---------------------------------------------宏任务微任务在下面👇
  2. 检查是否存在微任务( Microtasks ),如果存在则不停地执行,直至清空微任务队列(Microtask Queue)

  3. 更新 render(每一次事件循环,浏览器都可能会去更新渲染)

  4. 主线程重复执行上述步骤

    我们可以认为任务队列执行优先级: 同步 > 微任务 > 宏任务

    如下图所示:

3. 宏任务与微任务

    异步任务分为 宏任务(macrotask) 与 微任务 (microtask)

     1.宏任务

         script(整体代码)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 环境)

     2.微任务

         Promise、 MutaionObserver、process.nextTick(Node.js环境)

三. 举例(面试常谈)

1.  先来个简单的

console.log('script start');
 
setTimeout(function() {
  console.log('setTimeout');
}, 0);
 
Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});
console.log('script end');

输出结果

 

2.稍微增加点难度

setTimeout(()=>{
   console.log(1) 
},0)
let a=new Promise((resolve)=>{
    console.log(2)
    resolve()
}).then(()=>{
   console.log(3) 
}).then(()=>{
   console.log(4) 
})
console.log(5) 
// 输出
//2 5 3 4 1

3.再复杂一些

new Promise((resolve,reject)=>{
    console.log("promise1")
    resolve()
}).then(()=>{
    console.log("then11")
    new Promise((resolve,reject)=>{
        console.log("promise2")
        resolve()
    }).then(()=>{
        console.log("then21")
    }).then(()=>{
        console.log("then23")
    })
}).then(()=>{
    console.log("then12")
})

输出结果

    

4.难度++

async function async1() {
    console.log("async1 start");
    await  async2();
    console.log("async1 end");
}
async  function async2() {
    console.log( 'async2');
}
console.log("script start");
setTimeout(function () {
    console.log("settimeout");
},0);
async1();
new Promise(function (resolve) {
    console.log("promise1");
    resolve();
}).then(function () {
    console.log("promise2");
});
console.log('script end'); 

输出结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值