async/await、promise和setTimeout执行顺序

JavaScript运行机制

遵循事件循环机制,当JS解析执行时,会被引擎分为两类任务,同步任务(synchronous) 和异步任务(asynchronous)。对于同步任务来说,会被推到执行栈按顺序去执行这些任务。对于异步任务来说,当其可以被执行时,会被放到一个任务队列(task queue)里等待JS引擎去执行。当执行栈中的所有同步任务完成后,JS引擎才会去任务队列里查看是否有任务存在,并将任务放到执行栈中去执行,执行完了又会去任务队列里查看是否有已经可以执行的任务。这种循环检查的机制,就叫做事件循环(Event Loop)。对于任务队列,其实是有更细的分类。其被分为 微任务(microtask)队列 & 宏任务(macrotask)队列。

浏览器 JS 异步执行的原理

JS 是单线程的,也就是同一个时刻只能做一件事情,那么思考:为什么浏览器可以同时执行异步任务呢?

因为浏览器是多线程的,当 JS 需要执行异步任务时,浏览器会另外启动一个线程去执行该任务。也就是说,“JS 是单线程的”指的是执行 JS 代码的线程只有一个,是浏览器提供的 JS 引擎线程(主线程)。浏览器中还有定时器线程和 HTTP 请求线程等,这些线程主要不是来跑 JS 代码的。

比如主线程中需要发一个 AJAX 请求,就把这个任务交给另一个浏览器线程(HTTP 请求线程)去真正发送请求,待请求回来了,再将 callback 里需要执行的 JS 回调交给 JS 引擎线程去执行。即浏览器才是真正执行发送请求这个任务的角色,而 JS 只是负责执行最后的回调处理。所以这里的异步不是 JS 自身实现的,其实是浏览器为其提供的能力。

async/await、promise和setTimeout执行顺序_第1张图片

 

Promise 的运行机制

是异步编程的一种解决方案,相比回调函数和事件更合理和更强大

promise的构造函数是同步执行,promise.then中的函数是异步执行

它可以解决回调地狱的问题

async、await执行顺序

async/await就是promise的语法糖,

函数前必须加一个async,异步操作方法前加一个await关键字,意思就是等一下,执行完了再继续走。它可以很好的替代promise 中的then

使用 async 定义的函数,当它被调用时,它返回的其实是一个 Promise 对象。可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体后面的语句

setTimeout

同步执行:新程序或子程序被直接执行

宏任务:setTimeOutsetIntervalsetImmediate

微任务:promise.thenprocess.nextTickObject.observeMutationObserver

注意:Promise是同步任务

宏任务和微任务都是怎样执行的

执行script

进入script后,所有的同步任务主线程执行

所有宏任务放入宏任务执行队列

所有微任务放入微任务执行队列

先清空微任务队列

再取一个宏任务,执行,再清空任务队列

依次循环

异步笔试题:

async/await、promise和setTimeout执行顺序_第2张图片

async/await、promise和setTimeout执行顺序_第3张图片

async/await、promise和setTimeout执行顺序_第4张图片

过程:

1.首先浏览器执行Js代码由上至下顺序,遇到setTimeout,把setTimeout分发到宏任务Event Queue中
2.new Promise属于主线程任务直接执行打印2
3.Promis下的then方法属于微任务,把then分到微任务 Event Queue中
4.console.log(‘4’)属于主线程任务,直接执行打印4
5.又遇到new Promise也是直接执行打印5,Promise 下到then分发到微任务Event Queue中
6.又遇到setTimouse也是直接分发到宏任务Event Queue中,等待执行
7.console.log(‘10’)属于主线程任务直接执行
8.遇到bar()函数调用,执行构造函数内到代码,打印8,在bar函数中调用foo函数,执行foo函数到中代码,打印9
9.主线程中任务执行完后,就要执行分发到微任务Event Queue中代码,实行先进先出,所以依次打印3,6
10.微任务Event Queue中代码执行完,就执行宏任务Event Queue中代码,也是先进先出,依次打印1,7。
最终结果:2,4,5,10,8,9,3,6,1,7

分析:

(1)setTimeout 回调和 promise.then 都是异步执行的,将在所有同步代码之后执行;

(2)虽然 promise.then 写在后面,但是执行顺序却比 setTimeout 优先,因为它是微任务;

(3)new Promise 是同步执行的,promise.then 里面的回调才是异步的。

promise.then(微任务)的处理。当执行到 promise.then 时,V8 引擎不会将异步任务交给浏览器其他线程,而是将回调存在自己的一个队列中,待当前执行栈执行完成后,立马去执promise.then 存放的队列,promise.then 微任务没有多线程参与,甚至从某些角度说,微任务都不能完全算是异步,它只是将书写时的代码修改了执行顺序而已。

setTimeout 有“定时等待”这个任务,需要定时器线程执行;ajax 请求有“发送请求”这个任务,需要 HTTP 线程执行,而 promise.then 它没有任何异步任务需要其他线程执行,它只有回调,即使有,也只是内部嵌套的另一个宏任务。

简单小结一下微任务和宏任务的本质区别。

  • 宏任务特征:有明确的异步任务需要执行和回调;需要其他异步线程支持。

  • 微任务特征:没有明确的异步任务需要执行,只有回调;不需要其他异步线程支持。

 最后,请分析下面代码的输出顺序:

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

你可能感兴趣的:(大厂面试题前端,深度优先,宽度优先,算法)