《理解JS: 事件循环机制》

Posted 杨晓风-linda

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《理解JS: 事件循环机制》相关的知识,希望对你有一定的参考价值。

从面试题了解事件循环机制:

第一道:

//请写出输出内容
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');

第二道:

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

async function async2() 
    //async2做出如下更改:
    new Promise(function(resolve) 
	    console.log('promise1');
	    resolve();
	).then(function() 
	    console.log('promise2');
    );


console.log('script start');

setTimeout(function() 
    console.log('setTimeout');
, 0)

async1();

new Promise(function(resolve) 
    console.log('promise3');
    resolve();
).then(function() 
    console.log('promise4');
);

console.log('script end');

第三道:

async function async1() 
    console.log('async1 start');
    await async2();
    //更改如下:
    setTimeout(function() 
        console.log('setTimeout1')
    ,0)

async function async2() 
    //更改如下:
	setTimeout(function() 
		console.log('setTimeout2')
	,0)


console.log('script start');

setTimeout(function() 
    console.log('setTimeout3');
, 0)

async1();

new Promise(function(resolve) 
    console.log('promise1');
    resolve();
).then(function() 
    console.log('promise2');
);

console.log('script end');

第四道:

async function a1 () 
    console.log('a1 start')
    await a2()
    console.log('a1 end')

async function a2 () 
    console.log('a2')


console.log('script start')

setTimeout(() => 
    console.log('setTimeout')
, 0)

Promise.resolve().then(() => 
    console.log('promise1')
)

a1()

let promise2 = new Promise((resolve) => 
    resolve('promise2.then')
    console.log('promise2')
)

promise2.then((res) => 
    console.log(res)
    Promise.resolve().then(() => 
        console.log('promise3')
    )
)
console.log('script end')

第五道:

setTimeout(function()console.log(1),0);

new Promise(function(resolve,reject)
   console.log(2);
   setTimeout(function()resolve(),0)
).then(function()console.log(3)
).then(function()console.log(4));

process.nextTick(function()console.log(5));

console.log(6);

    如果不太理解其中所蕴含的道理,点击下:体现事件循环机制过程在线网站

    如果能正确答出上面题的输出内容,并能给予背后原理的解释,那证明读者对于JS中事件循环机制了解得挺深入的哦~

之前小编对于JS中事件循环机制了解也是模棱两可,近来对此部分进行了进一步的学习,先以一张图来说明学习成果:

想要了解JS中的事件循环机制,咱们需要了解如下内容:

  • event loop:主线程从“任务队列”中循环读取任务。而循环读取任务的先后顺序,则取决于任务队列(Job queue)中对于不同任务读取规则的限定
  • Job queue:分为两种类型:macro-task和micro-task,即为宏任务和微任务,微任务的执行优先级高于宏任务

  • 浏览器是多进程的,JS是单线程的。浏览器除了JS引擎之外,还有Web APIs线程、GUI线程等, JS引擎在执行过程中,如果遇到相关的事件(DOM操作、AJAX请求、滚轮事件、鼠标点击事件、setTimeout等),并不会因此阻塞,它会将这些事件移交给Web APIs线程处理,而自己接着往下进行。

  • setTimeout:设置的时间是最小延迟时间,并不是确切的等待时间,实际上最小延迟>=4ms,小于4ms的被当做4ms

  • Web APIs:按照一定的规则将这些事件放入一个任务队列(Callback queue,也叫task queue)

  • 任务队列:在html标准定义中,任务队列的数据结构不是队列,而是Set集合

  • JS单线程:不想并行操作DOM,DOM树不是线程安全的,如果是多线程,会引起冲突。只有一个执行栈(call stack),在同一时刻,JS引擎只能做一件事情,并且只要运行就直到完成(run to complete)。如果是这样的话,那么我网页后端请求数据的时候,为什么我还能点击按钮、滚动页面呢?原因是因为浏览器是多进程的,如下图所示的事件循环模型:

 说明:

         1、JS线程负责处理JS代码,当遇到一些异步操作的时候,则将这些异步事件移交给Web APIs处理,自己则继续往下执行

         2、Web APIs线程将接收到的事件按照一定的规则添加到任务队列中,宏事件添加到宏任务队列中,微事件添加到微任务队列中

         3、JS线程处理完当前的所有任务以后(执行栈为空),它会先去微任务队列中获取事件,并将微任务队列中按照先进先执行的顺序一件件执行完毕,直到微任务队列为空后,再去宏任务队列中取出一个事件执行

(每次取完一个宏任务队列中的事件执行完毕后,都先检查微任务队列)

         4、然后不断循环第三步。

  • new Promise:在使用new关键字来创建Promise对象时,传递给Promise的函数称为executor,当promise被创建的时候,executor函数会自动执行,而then里面的内容才是异步执行的部分。
  • async/await:async是Generator函数的语法糖,await关键字会将后面的内容包装成promise交给Web APIs处理,执行栈会跳出async函数继续执行,直到promise执行完并返回结果。

 在这里,小编第一次也并没有理解async/await关键字的作用,在第二遍学习的时候理解了,请看如下示例:

源代码:

async function async1() 
	console.log('async1 start')
	await async2()
	console.log('async1 end')


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

 其实,根据await关键字的作用,会将后面的内容包装成promise,然后应该是如下模样:

async function async1()
    console.log('async1 start') 
    new promise((reslove)=>
        console.log('async2')
    ).then(()=>
        console.log('async1 end')
    )

基本上在理解了如上相关内容后,对于JS中异步机制以及相关面试题有了深入的理解。

如下为开头面试题答案:

第一道:

script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

第二道:

script start
async1 start
promise1
promise3
script end
promise2
async1 end
promise4
setTimeout

第三道:

script start
async1 start
promise1
script end
promise2
setTimeout3
setTimeout2
setTimeout1

第四道:

script start
a1 start
a2
promise2
script end
promise1
a1 end
promise2.then
promise3
setTimeout

第五道:

//输出的是  2 6 5 1 3 4

//区别在于promise的构造中,没有同步的resolve
,因此promise.then在当前的执行队列中是不存在的,
只有promise从pending转移到resolve,才会有then方法,而这个resolve是在一个setTimout时间中完成的,因此3,4最后输出。

巨人肩膀:

谈谈Event Loop中的Job queue

事件循环(event loop)以及异步执行顺序

以上是关于《理解JS: 事件循环机制》的主要内容,如果未能解决你的问题,请参考以下文章

深入理解事件循环机制

对javascript EventLoop事件循环机制不一样的理解

js事件循环机制辨析

图解js里的事件循环---机制

js执行机制:event loop(多图理解)

js的事件循环(Eventloop) 机制