html5前端循环输出,前端高级面试题-彻底掌握JS事件循环原理

前言

为什么写这篇文章?

因为这道题涉及了很多知识点:同步任务、异步任务、宏任务、微任务、任务队列、执行栈、js运行机制、EventLoop,所以想整理一下,写一篇文章,希望对小伙伴们有所帮助!

经典题

下面这段promise、async和await代码,请问控制台打印顺序?

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

复制代码

注:因为是一道前端面试题,所以答案是以浏览器的eventloop机制为准的,在node平台上运行会有差异

相信大多数前端都知道,这道题考的就是js里面的事件循环和任务队列

知识点

同步任务、异步任务、宏任务、微任务、任务队列、执行栈、js运行机制、Event Loop

这题考察的是js中的事件循环和任务队列,注意以下几点:

Promise优先于setTimeout宏任务。所以,setTimeout回调会在最后执行

Promise一旦被定义,就会立即执行

Promise的reject和resolve是异步执行的回调。所以,resolve()会被放到回调队列中,在主函数执行完和setTimeout前调用

await执行完后,会让出线程。async标记的函数会返回一个Promise对象

async介绍

async function 声明将定义一个返回 AsyncFunction 对象的异步函数

当调用一个 async 函数时,会返回一个 Promise 对象

当这个 async 函数返回一个值时,Promise 的 resolve 方法会负责传递这个值

当 async 函数抛出异常时,Promise 的 reject 方法也会传递这个异常值

它通过返回一个 Promise 对象来返回结果最大的特点是:

通过 async / await 将异步的操作,但写法和结构却是和我们平时写的(同步代码)是一样

await

await操作符用于等待一个Promise对象

await表达式会暂停当前async

function的执行,等待Promise处理完成,若Promise正常处理,其回调的resolve函数参数作为await表达式的值,继续执行async function

await意味让出线程操作

返回值(return_value):返回 Promise 对象的处理结果。如果等待的不是 Promise 对象,则返回该值本身,

所以,当await操作符后面的表达式是一个Promise的时候,它的返回值,实际上就是Promise的回调函数resolve的参数

我们都知道Promise是一个立即执行函数,但是他的成功(或失败:reject)的回调函数resolve却是一个异步执行的回调。当执行到resolve()时,这个任务会被放入到回调队列中,等待调用栈有空闲时事件循环再来取走它

promise

async会返回Promise对象,如果返回值不是Promise对象则调用Promise resolve来换成Promise对象

async/await建立在Prmise机制上

总结一句话:带async关键字的函数,它使得你的函数的返回值必定是 promise 对象上

EvenLoop

这里先简单的说一些Event Loop的概念,在最近这段时间我会写一篇关于Event Loop的文章

Javascript是单线程的,所有的同步任务都会在主线程中执行

主线程之外,还有一个任务队列。每当一个异步任务有结果了,就往任务队列里塞一个事件。 当主线程中的任务,都执行完之后,系统会 “依次” 读取任务队列里的事件。与之相对应的异步任务进入主线程,开始执行

异步任务之间,会存在差异,所以它们执行的优先级也会有区别。大致分为 微任务(micro task,如:Promise、MutaionObserver等)和宏任务(macro task,如:setTimeout、setInterval、I/O等)。同一次事件循环中,微任务永远在宏任务之前执行

主线程会不断重复上面的步骤,直到执行完所有任务

题目结果分析过程

第一步,输出script start

虽然有两个函数声明,有async关键字,但是没有调用我们就不看,直接打印同步代码console.log(‘script start’)

第二步,输出async1 start

在执行async1这个函数的时候,async表达式定义的函数也是立即执行 在前面我们说过看到带有async关键字函数,不用慌,它仅仅是把return值包装成了promise,所以就很普通的打印 console.log( 'async1 start' )

第三步,输出async2

async2是async定义的函数,输出async2并返回promise对象, await后,中断async函数,先执行async外的同步代码, 目前就直接打印 console.log('async2')

第四步,输出promise1

执行new Promise(),Promise构造函数是直接调用的同步代码,所以就打印console.log( 'promise1' )

第五步,输出script end

因为上一步先打印了promise1,然后执行到resolve的时候,然后跳出promise继续向下执行,所以就打印console.log( 'script end' )

第六步,输出async1 end

因为await定义的这个promise已经执行完,并且返回结果,所以继续执行async1函数后的任务,就是console.log(‘async1 end’)

第七步,输出promise2

因为前面的new promise放进resolve回调,这个resolve被放到调用栈执行,所以就打印console.log('promise')

第八步,输出setTimerout

最后执行定时器(宏任务)setTimeout,打印console.log( 'setTimerout' )

题目变式一

在这个变式中我将async2中的函数也变成了Promise函数,代码如下:

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

复制代码

正确答案

script start

async1 start

promise1

promise3

script end

promise2

async1 end

promise4

setTimeout

复制代码

题目变式二

我将async1中await后面的代码和async2的代码都改为异步的,代码如下:

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

复制代码

正确答案

script start

async1 start

promise1

script end

promise2

setTimeout3

setTimeout2

setTimeout1

复制代码

题目变式三

这道题整体来说与上面题大同小异与,代码如下:

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')

复制代码

正确答案

script start

a1 start

a2

promise2

script end

promise1

a1 end

promise2.then

promise3

setTimeout

复制代码

最后

如果觉得本文还不错,记得点个赞哦!

欢迎大家加入,一起学习前端,共同进步!

1460000021710957

1460000021710956

b739ec46bb5c46d9c0aa4ce35ba1ea56.png

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[前端高级面试题-彻底掌握JS事件循环原理]http://www.zyiz.net/tech/detail-104942.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值