2.事件循环

事件循环

参考:

  1. 这一次,彻底弄懂 JavaScript 执行机制
  2. JavaScript 运行机制详解:再谈Event Loop
  3. js与Nodejs的单线程和异步–初探
  4. Node.js的事件轮询Event Loop原理解释
  5. JavaScript任务队列的顺序机制(事件循环)
  6. JS与Node.js中的事件循环

什么是事件轮循?

1. Js属于单线程,因为Js不仅关于交互,也需实现对DOM的操作。假如JS是多线程,会存在不同线程操作同一个DOM的情况,此时会产生冲突;
	* JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
2. 执行任务分为:同步任务,异步任务;
3. 异步任务:宏任务/微任务;

事件轮询

  1. JS引擎首先从宏任务队列中取出第一个任务,执行完毕后,将微任务队列中的所有任务取出,按顺序全部执行;
  2. 然后再从宏任务中取下一个,执行完毕后,再次将微任务中的全部取出;
  3. 循环往复,直到两个队列中的任务都取完。

注意:JS引擎中,微任务的执行优先级高于宏任务,如果当前任务执行完,发现有微任务就会立即执行微任务,直到微任务队列为空,然后再回头执行宏任务;

宏任务/微任务

宏任务

  • 分类: script (同步代码), setTimeout, setInterval, setImmediate, MessageChannel, postMessage, I/O, UI渲染
    1. 宏任务所处的队列就是宏任务队列
    2. 第一个宏任务队列中只有一个任务: 执行主线程的js代码
    3. 宏任务队列可以有多个
    4. 注意:当前宏任务执行完毕后,会先查看是否有微任务队列;
      如果有先执行微任务队列中的所有任务,如果没有就查看是否还有有宏任务队列

微任务

  • 分类: new Promise().then(回调) process.nextTick,MutationObserver
    1. 微任务所处的队列就是微任务队列
    2. 只有一个微任务队列
    3. 当前任务(宏任务/微任务)执行完毕,如果微任务队列不为空则继续执行,直到为空
例1
console.log('----------------- start -----------------');
setTimeout(() => {
  console.log('setTimeout');
}, 0)

new Promise((resolve, reject) =>{
  for (var i = 0; i < 5; i++) {
    console.log(i);
  }
  resolve();  // 修改promise实例对象的状态为成功的状态
}).then(() => {
  console.log('promise实例成功回调执行');
})
console.log('----------------- end -----------------');

/***'
 * 
 * 输出结果为:
 * 
 * ==>----------------- start -----------------
   ==>0
   ==>1
   ==>2
   ==>3
   ==>4
   ==>----------------- end -----------------
   ==>promise实例成功回调执行
   ==>setTimeout
 */

在这里插入图片描述

例2
console.log('start')                   //------(1)
setTimeout(() => {
  console.log('children2')             //------(2)
  Promise.resolve().then(() => {
    console.log('children3')           //------(3)
  })
}, 0)

new Promise(function (resolve, reject) {
  console.log('children4')            //------(4)
  setTimeout(function () {
    console.log('children5')          //------(5)
    resolve('children6')              //------(6)
  }, 0)
}).then((res) => {
  console.log('children7')            //------(7)
  setTimeout(() => {
    console.log(res)                  //------(8)
  }, 0)
})

//***************************************
//Chorme浏览器结果:【先执行完同步任务,】
// start          => 同步任务 
// children4      => 同步任务 
// 第一轮宏任务结束,尝试清除微任务队列,发现没有微任务,直接执行宏任务setTimeout();
// 此过程中有微任务产生;
//***************************************
// children2  
// children3    
//=>执行宏任务,【注意】此时整个setTimout为一个宏任务,完成该宏任务就会清空        
//微任务,而此时在执行该宏任务时有一个.then()的微任务,然后清空微任务队列,
//所以下个输出为children3
//***************************************
// 执行下一轮宏任务;
// children5
// children7
// children6
//***************************************
// Node输出结果:  结果不稳定,3,5都是异步操作,无法确定先后
//结果1
// start
// children4
// children2
// children3
// children5
// children7
// children6

//结果2
// start
// children4
// children2
// children5
// children3
// children7
// children6

结果1:
在这里插入图片描述
结果2:
在这里插入图片描述
**注意:**node上运行产生结果不稳定;

		  1.第一轮:=>(1) (4)    [宏任务队列,主线程起始执行队列,执行完毕,发现无微任务]
		  2.第二轮:=>(2)        [宏任务队列,两个setTimeout]  
		           =>(5)(3)或(3)(5),注意此时第一个setTimeout中的微任务(3)和宏任务中的(5)都是异步执行,不能完全确定谁先执行完毕,故此会产生两种结果;然后执行resolve(6)的回调,执行完毕,发现微任务
		  3.第三轮:=>(7),Promise的回调微任务中先执行,微任务执行完毕,发现第三个setTimeout;
		  4.第四轮:=>(6),至此宏任务队列清空,没有微任务队列,完毕

综上JS事件循环的特点为:

  1. chrome的运行比较稳定,而node环境下运行不稳定,可能会出现两种情况。
  2. chrome运行的结果稳定的原因:
    是Promise、process.nextTick()的微任务Event Queue运行的权限比普通宏任务Event Queue权限高,如果取事件队列中的事件的时候有微任务,就先执行微任务队列里的任务,除非该任务在下一轮的Event Loop中,微任务队列清空了之后再执行宏任务队列里的任务。
const p = function () {
  return new Promise((resolve, reject) => {
    const p1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(1)
      }, 0)
      resolve(2)
    })
    p1.then((res) => {
      console.log(res)
    })
    console.log(3)
    resolve(4)
  })
}
p().then((res) => {
  console.log(res)
})
console.log('end')

//3
//end
//2
//4

注意: 注意坑点:在p1函数中,函数体中在宏任务之前已经resolve(2),于是p1函数的回调就输出了2,注意注意,此时整个回调函数执行完毕,不会再输出1了!!!

例3
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');


/*
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
*/
详见:https://blog.csdn.net/qq_25257229/article/details/114540346		
async function foo() {
   await 1
}
//等价于
function foo() {
   return Promise.resolve(1).then(() => undefined)
}

在await表达式之后的代码可以被认为是存在在链式调用的then回调中,多个await表达式都将加入链式调用的then回调中,返回值将作为最后一个then回调的返回值。


在这里插入图片描述

JS事件循环执行机制
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值