【异步系列三】10道 Promise 面试题彻底理解 Promise 异步执行顺序

这篇具有很好参考价值的文章主要介绍了【异步系列三】10道 Promise 面试题彻底理解 Promise 异步执行顺序。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

上一篇文章详细说明了关于 Promise 的概念以及执行顺序,Promise 作为 JavaScript 中的关键特性,无论是在日常工作中还是面试中,我们都必须掌握它。

前段时间在网上看到了一些关于 Promise 的面试题,质量很好,在这里整理到下面,并加上我一些自己的见解,欢迎斧正!

1. 同步代码块

console.log('1')

const p = new Promise((resolve, reject) => {
	console.log('2')
})

console.log('3')

第一个面试题可谓是非常简单了,

  • 同步代码块是从上往下执行的
  • 当我们 new 一个 Promise 时,里面的起始函数会立即调用,而且里面的代码块是同步代码

所以这段代码依次输出结果为 1 2 3

2. 出现异步代码

console.log('start')

conse p = new Promise((resolve, reject) => {
	console.log('1')
	resolve('2')
})

p.then(res => {
	console.log(res)
})

console.log('end')

在这部分代码中,出现了一段异步代码:也就是 .then() 中的回调函数

要记住一个原则:同步先行,异步靠后,Javascript 引擎总是先执行同步代码,然后在执行异步代码。

此时,我们只需要区分同步代码和异步代码即可。

所以这段代码吗输出结果为: start 1 end 2

3. 有 resolve 的地方

console.log('strt')

const p = new Promise((resolve, reject) => {
	console.log(1)
	resolve(2)
	console.log(3)
})

p.then(res => {
	console.log(res)
})

console.log('end')

这段代码与前面的代码基本一致,唯一区别是在 resolve() 之后有一个 console.log()

要记住:resolve 方法不会中断函数的执行,它背后的代码仍然会继续执行。

所以输出的结果是: start 1 3 end 2

4. 不被调用 resolve 的地方

console.log('start')

const p = new Promise((resolve, reject) => {
	console.log(1)
})

p.then(res => {
	console.log(2)
})

console.log('end')

在这段代码中,resolve 方法从未被调用过,因此 p始终处于挂起状态 。 所以 p.then(…) 从未被执行过,故不会在控制台中打印。

所以输出的结果是: start 1 end

5. 带有函数的

console.log('start')

const fn = () => {
	return new Promise((resolve, reject) => {
		console.log(1)
		resolve(2)
	})
}

console.log(3)

fn().then(res => {
	console.log(res)
})

console.log('end')

这段代码部分人开起来可能会有些迷惑,因为加了一层 fn

但我们要要记住,无论有多少层函数调用,我们的基本原则都是一样的:

  • 同步先行,异步靠后
  • 同步代码按照调用顺序执行

输出结果为 start 3 1 end 2

详细解析:

首先,第一步输出 start ,相信大家都没什么疑问。
fn 是一个函数,里面返回的是一个 new Promise 对象,new Promise 对象中的回调函数会立即执行,但由于此时 fn 函数还未调用,故先不执行。
接下来输出 3
下一行代码 fn() 函数调用,所以立即 打印 1
调用 resolve() 方法,这是一个异步代码,作为异步,需要等到同步代码执行完成后才会执行,故打印 end
同步代码全部执行完成后,执行异步代码, 打印 2
所以输出结果为 start 3 1 end 2

6. Fulfilling Promise

console.log('start')

Promise.resolve(1).then((res) => {
	console.log(res)
})

Promise.resolve(2).then(res => {
	console.log(res)
})

console.log('end')

输出结果为: start end 1 2

详细解析:

首先,我们要明白 Promise.resolve(1) 这行代码
Promise.resolve方法返回一个promise的实例,状态为已完成
这行代码等价于:
new Promise((resolve) => { resolve(1) })
看到这里,是不是就明白了?
Promise.resolve(1) 是同步代码,但后面的 then() 是异步
所以输出结果为: start end 1 2

7. 宏任务 微任务

console.log('start')

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

Promise.resolve().then(() => {
	console.log('resolve')
})

console.log('end')

详细解析:

这段代码相比较上一个来说,多了一个定时器,我们知道,定时器也是一个异步操作。那么这时候问题就来了,同步代码是按照调用顺序执行的,那么异步回调函数时按照什么顺序执行的呢?大家可以参考下我的上一篇博客:Promise原理解析及执行顺序。

在上面的代码中,setTimeout的定时器是0秒,Promise.resolve() 也会在执行后立即返回一个已完成的 Promise 对象。

两个异步任务都是立即完成的,那么谁的回调函数会先执行呢?

有人会说 setTimeout 在前面,所以先打印 setTimeout,然后打印 resolve,实际上,当我们在控制台运行下代码时会发现,实际情况并不是我们想的那样。

它们是有优先级的。

微任务 > 宏任务

  • 具有较高优先级的任务叫做微任务。包括:Promise、ObjectObserver、MutationObserver、Process.nextTick、async/await。
  • 优先级较低的任务称为宏任务。包括:setTimeout、setInterval、XHR

虽然 setTimeout 和 Promise.resolve() 是同时完成的,甚至 setTimeout 的代码写在前面,但是由于它的优先级较低,所以它的回调函数是在后面执行的。

所以输出结果是:start end resolve success

8. 微任务 + 宏任务 升级

const p = new Promise((resolve, reject) => {
	console.log(1)
	setTimeout(() => {
		console.log('timerStart')
		resolve('resolve')
		console.log('timerEnd')
	}, 0)
	console.log(2)
})

p.then((res) => {
	console.log(res)
})

console.log('end')

详细解析:

首先执行同步代码: 1 2 end

然后找到 微任务 和 宏任务

从上一个代码解析中我们知道,定时器是宏任务,Promise 是微任务:

但这里有一个陷阱: 当前的promise 还处于 pending 状态,所以promise.then() 这部分代码(微任务)暂时不会被执行。

所以会先执行定时器中的回调函数,然后在执行微任务。

所以输出结果为: 1 2 end timerStart timerEnd resolve

9. 微任务 宏任务 交替执行排序

const timer1 = setTimeout(() => {
	console.log(1)

	const promise1 = Promise.resolve().then(() => {
		console.log('promise1')
	})
}, 0)

const timer2 = setTimeout(() => {
	console.log('timer2')
}, 0)

输出结果为: timer1 promise1 tiemr2

详细解析:

关于 宏任务 和 微任务,正确的理解是:

  1. 首先执行所有的微任务
  2. 执行宏任务
  3. 再次执行所有(新添加的)微任务
  4. 执行下一个宏任务

在上面代码中,没有和宏任务同步的微任务,所以开始执行 timer1 这个宏任务;

在 timer1 的宏任务中,新增了一个微任务,且该promise 对象的状态已经变为 fufilled(已成功),所以Promise.then() 的回调函数会在第二个宏任务 setTimeout 的回调函数之前执行。

所以执行结果为: timer1 promise1 tiemr2

10 最终升级版

console.log('start')

const promise1 = Promise.resolve().then(() => {
	console.log('promise1')
	const timer2 = setTimeout(() => {
		console.log('timer2')
	}, 0)
})

const timer1 = setTimeout(() => {
	console.log('timer1')
	const promise2 = Promise.resolve().then(() => {
		console.log('promise2')
	})
}, 0)

console.log('end')

如果能够正确写出答案且能说明原因的话,说明你对 Promise 的理解已经很强了,面试基本上没有问题。

详细解析:

按照以下思路进行思考:

  1. 同步代码
  2. 所有微任务
  3. 第一个宏任务
  4. 所有新添加的微任务
  5. 下一个宏任务

① 执行所有同步代码:

打印 start end

② 执行所有微任务

promise面试题,前端经典面试题,javascript,javascript,前端,vue.js,Promise

在这里打印 promise1

③ 执行第一个宏任务

promise面试题,前端经典面试题,javascript,javascript,前端,vue.js,Promise

在这里打印 timer1

注意:在这一步中,宏任务将一个新的为任务添加到任务队列中

④ 执行所有新添加的微任务

promise面试题,前端经典面试题,javascript,javascript,前端,vue.js,Promise
在这里打印 promise2

⑤ 执行下一个宏任务

promise面试题,前端经典面试题,javascript,javascript,前端,vue.js,Promise
所以,最后输出结果为: start end promise1 timer1 promise2 timer2

结论

在面试中,如果有类似的面试题,只需要记住一下三个核心要点,问题即可迎刃而解。文章来源地址https://www.toymoban.com/news/detail-701474.html

  1. JavaScript 引擎总是先执行同步代码,然后在执行异步代码(同步先行,异步靠后)
  2. 微任务的优先级高于宏任务
  3. 微任务可以在 Event Loop 中插队。

到了这里,关于【异步系列三】10道 Promise 面试题彻底理解 Promise 异步执行顺序的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JS中使用Promise.all控制所有的异步请求都完成后,在执行后续逻辑

    使用场景为,在js中连续的几个异步耗时操作,后面的耗时操作需要使用第一个操作的返回结果。例如调用ajax异步接口,需要先创建完主表,然后拿到主表id在去循环创建明细表,等全部创建完成后,弹出提示来,或者失败提示。 通常情况,在耗时操作完成后在去调用,需要

    2024年02月13日
    浏览(43)
  • vue async await promise 等待异步接口执行完毕再进行下一步操作

    需求:上传多个文件,每上传一个文件异步请求一下后台接口,并返回一个新文件名,等把所有的异步接口执行成功后,将上传已成功后新文件名数组得到再去更新业务数据 uni-file-picker 文件上传组件的配置 选择文件后,上传到服务器后端,一个一个的传,等异步执行完一下

    2024年02月12日
    浏览(50)
  • 【jvm系列-10】深入理解jvm垃圾回收器的种类以及内部的执行原理

    JVM系列整体栏目 内容 链接地址 【一】初识虚拟机与java虚拟机 https://blog.csdn.net/zhenghuishengq/article/details/129544460 【二】jvm的类加载子系统以及jclasslib的基本使用 https://blog.csdn.net/zhenghuishengq/article/details/129610963 【三】运行时私有区域之虚拟机栈、程序计数器、本地方法栈 https

    2024年02月05日
    浏览(90)
  • js中如何顺序执行异步任务

    在js中,任务可分为两种,同步任务和异步任务。 (1) 同步任务 又叫 非耗时任务 ,指的是在主线程排队执行的那些任务 只有前一个任务执行完毕,才能执行后一个任务 (2) 异步任务 又叫 耗时任务 ,异步任务由JavaScript委托给宿主环境进行执行 当异步任务执行完成后,会通知

    2024年02月09日
    浏览(41)
  • 【面试问题】事务中执行了异步任务分发数据,由于事务未提交,导致异步任务无法执行

    客户数据分发CRMS系统的时候异常,分发任务强依赖于事务内有没有提交,异常由事务未及时提交导致异步任务无法及时查到数据,现将异步任务调整为事务提交后处理 添加事务同步管理器,声明异步是在事务提交后执行

    2024年02月13日
    浏览(36)
  • 深入理解高并发编程 - 线程的执行顺序

    在Java中,线程的执行顺序是由操作系统的调度机制决定的,具体顺序是不确定的,取决于多个因素,如操作系统的调度策略、线程的优先级、线程的状态转换等。因此,不能对线程的执行顺序做出可靠的假设。 以下是一个简单的Java代码示例,演示了多个线程的执行顺序是不

    2024年02月14日
    浏览(53)
  • 一文读懂异步和同步——async/await在运行时和普通代码的执行顺序的问题

    一、执行顺序问题 JavaScript中的await语句是异步编程中用于等待Promise对象执行结果的,它通常与async函数一起使用。在使用await时,程序的执行将暂停,直到该后面的Promise对象完成执行并返回结果。 函数代码执行顺序通常情况下按照代码所在文件从上至下

    2024年02月03日
    浏览(42)
  • 【嵌入式系列】一文彻底理解DMA

    DMA用于在 外设 与 存储器 之间以及 存储器 与 存储器 之间提供高速数据传输。可以在无需任何 CPU 操作的情况下通过 DMA 快速移动数据。这样节省的 CPU 资源可供其它操作使用。 我们用一个现实例子来做个类比。有一家冶炼公司,每天要不断的从矿场拉矿石进入厂区冶炼,以

    2023年04月19日
    浏览(95)
  • 【Go面试向】Go程序的执行顺序

    大家好 我是寸铁👊 总结了一篇Go程序的执行顺序的文章✨ 喜欢的小伙伴可以点点关注 💝 go程序通常包含: 包、常量、变量、init()、main()等元素 下面从这几个方面分别去梳理! 程序中的包 一个.go文件中,其他的包只有被 main 包import才会执行,按照import的 先后 顺序执行。

    2024年01月24日
    浏览(33)
  • 【MySQL系列】- SELECT语句执行顺序

    2.1 执行FROM操作 这一步需要做的是对FROM子句前后的两张表进行笛卡尔积操作,也称作为交叉连接,生成虚拟表VT1。如果FROM子句前的表包含a行数据,FROM子句后的表中包含b行数据,那么虚拟表VT1将包含a*b行数据。 2.2 应用ON过滤器 SELECT查询共有3个过滤流程,分别是ON、WHERE、

    2024年02月08日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包