首页 > 编程语言 >10 个 JavaScript Promise 的面试题

10 个 JavaScript Promise 的面试题

时间:2022-11-08 18:13:01浏览次数:52  
标签:10 面试题 resolve console log 代码 JavaScript 任务 Promise

10 个 JavaScript Promise 的面试题_优先级

英文 | https://betterprogramming.pub/10-javascript-promise-challenges-before-you-start-an-interview-c9af8d4144ec

翻译 | 杨小爱


Promise 是 JavaScript 异步编程的关键特性。不管你爱它还是恨它,你都必须理解它。

在这里,我整理了一些关于 Promise 的面试挑战题,从基础到高级。有 10 个代码片段,在阅读我的分析之前,请先自己尝试一下。

1、同步代码块一

console.log('start');


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


console.log('end');

第一个问题非常简单。

我们知道:

  • 同步的代码块总是从上到下顺序执行。
  • 当我们调用 new Promise(callback) 时,回调函数会立即执行。

所以这段代码是依次输出start、1、end。

2、出现异步代码的地方

console.log('start');


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


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


console.log('end');

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

请记住,JavaScript 引擎总是先执行同步代码,然后再执行异步代码。

遇到这个问题,我们只需要区分同步代码和异步代码即可。

10 个 JavaScript Promise 的面试题_优先级_02

所以输出的结果是 start 、 1 、 end 和 2 。

3、有“resolve”的地方

console.log('start');


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


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


console.log('end');

这段代码与前面的代码几乎相同;唯一的区别是在resolve(2)之后有一个console.log(3)。

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

所以输出结果是 start , 1 , 3, end 和 2 。

因为我遇到过一些人认为resolve会中断函数的执行,所以在这里强调一下。

4、不被调用“resolve”的地方

console.log('start');


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


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


console.log('end');

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

所以输出结果是 start, 1, end。

5、让你困惑的地方

console.log('start')


const fn = () => (new Promise((resolve, reject) => {
console.log(1);
resolve('success')
}))


console.log('middle')


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


console.log('end')

这段代码特意增加了一个迷惑挑战者的功能,那就是fn。

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

  • 先执行同步代码,再执行异步代码
  • 同步代码按调用顺序执行

10 个 JavaScript Promise 的面试题_javascript_03

所以输出结果是 start 、 middle、 1 、 end 和 success。

好的,你觉得这些挑战容易吗?

事实上,这些只是开胃菜。Promise 的难点在于它与 setTimeout 一起出现。接下来,我们加大挑战的难度。

你准备好了吗?开发者!

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

这里 Promise.resolve(1) 将返回一个 Promise 对象,其状态为已完成,结果为 1 ,它是同步代码。

10 个 JavaScript Promise 的面试题_优先级_04

所以输出结果是 start 、 end 、 1 和 2。

7、测试回调基础的地方

console.log('start')


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


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


console.log('end')

注意!

这是一个非常困难的问题。如果你能正确回答这个问题并说明原因,那么你对 JavaScript 中异步编程的理解已经达到了中级水平。

在解释这个问题之前,让我们先讨论一下相关的理论基础知识。

之前我们说过同步代码是按照调用顺序执行的,那么这些异步回调函数是按照什么顺序执行的呢?

有人可能会说,谁先完成谁先执行。嗯,这是真的,但是如果两个异步任务同时完成呢?

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

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

有的人可能会说setTimeout在前面,所以先打印setTimeout,再打印resolve。实际上,这种说法是错误的。

我们知道很多事情不是按照先进先出的顺序执行的,比如流量。

我们一般将车辆分为两类:

  • 一般车辆
  • 用于紧急任务的车辆。如消防车和救护车。

通过拥挤的十字路口时,我们将让消防车和救护车先行通过。紧急车辆的优先级高于其他车辆。关键词:优先级。

10 个 JavaScript Promise 的面试题_回调函数_05

在 JavaScript EventLoop 中,还有优先级的概念。

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

10 个 JavaScript Promise 的面试题_回调函数_06

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

10 个 JavaScript Promise 的面试题_回调函数_07

所以输出结果是 start 、 end 、 resolve 和 success。

8、一个有微任务和宏任务代码的

const promise = new Promise((resolve, reject) => {
console.log(1);
setTimeout(() => {
console.log("timerStart");
resolve("success");
console.log("timerEnd");
}, 0);
console.log(2);
});


promise.then((res) => {
console.log(res);
});


console.log(4);

如果您已经了解了前面的代码片段,那么这个挑战很容易完成。

我们只需要做三个步骤:

  • 找到同步代码。
  • 找到微任务代码
  • 找到宏任务代码

首先,执行同步代码:

10 个 JavaScript Promise 的面试题_javascript_08

输出 1、2 和 4。

然后执行微任务:

10 个 JavaScript Promise 的面试题_优先级_09

但是这里有个陷阱:由于当前的promise还处于pending状态,所以这里的代码暂时不会被执行。

然后执行宏任务:

10 个 JavaScript Promise 的面试题_回调函数_10

并且promise的状态正在成为fulfilled。

然后,使用事件循环,再次执行微任务:

10 个 JavaScript Promise 的面试题_回调函数_11

9、你将被要求在微任务和宏任务之间进行优先排序

在介绍微任务和宏任务的优先级之前,这里先看一下微任务和宏任务交替执行的情况。

const timer1 = setTimeout(() => {
console.log('timer1');


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


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

这段代码的输出是什么?

有些人可能会认为 microtask 和 macrotask 是这样的:

  1. 首先执行所有微任务
  2. 执行所有宏任务
  3. 再次执行所有微任务
  4. 绕圈穿过

但是上面的说法是错误的。

正确的理解是:

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

像这样:

10 个 JavaScript Promise 的面试题_javascript_12

或者像这样:

10 个 JavaScript Promise 的面试题_javascript_13

所以在上面的代码中,Promise.then 的回调函数会在第二个 setTimeout 的回调函数之前执行,因为它是一个微任务,已经被切线了。

10 个 JavaScript Promise 的面试题_优先级_14

10、最后一次测试你的 Promise 基础

这是我们最后一个挑战,如果你能正确说出这段代码的输出结果,那么你对 Promise 的理解就已经很强了,同类型的面试题根本难不倒你。

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

本次挑战是上一次挑战的升级版,但核心原理保持不变。

记住我们之前学到的:

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

所以:

1)、执行所有同步代码:

10 个 JavaScript Promise 的面试题_回调函数_15

2)、执行所有微任务

10 个 JavaScript Promise 的面试题_优先级_16

3)、执行第一个宏任务

10 个 JavaScript Promise 的面试题_回调函数_17

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

4)、执行所有新添加的微任务


10 个 JavaScript Promise 的面试题_回调函数_18

5)、执行下一个宏任务

10 个 JavaScript Promise 的面试题_回调函数_19

所以输出是这样的。

结论

对于所有类似的问题,您只需要记住三个规则:

1、JavaScript 引擎总是先执行同步代码,然后再执行异步代码。

2、微任务的优先级高于宏任务。

10 个 JavaScript Promise 的面试题_回调函数_06

3、微任务可以在 Event Loop 中插队。

10 个 JavaScript Promise 的面试题_回调函数_21

最后,感谢你的阅读。


10 个 JavaScript Promise 的面试题_优先级_22


10 个 JavaScript Promise 的面试题_优先级_23


标签:10,面试题,resolve,console,log,代码,JavaScript,任务,Promise
From: https://blog.51cto.com/u_15809510/5833956

相关文章

  • 一位经验丰富的前端开发人员因未能通过算法面试题而受人嘲笑
    英文|https://medium.com/frontend-canteen/an-experienced-frontend-developer-gets-ridiculed-by-an-interviewer-for-failing-to-complete-an-dccd790feea4翻译|杨小......
  • 20220810 06. Linux 文件与目录管理
    6.1目录与路径6.1.1相对路径与绝对路径路径(PATH)绝对路径:路径的写法“一定由根目录/写起”,例如:/usr/share/doc这个目录。相对路径:路径的写法“不是由/写起......
  • Python中10个常见的安全漏洞及修复方法
    编写安全的代码很困难,当你学习一门编程语言、一个模块或框架时,你会学习其使用方法。在考虑安全性时,你需要考虑如何避免代码被滥用,Python也不例外,即使在标准库中,也存在着许多......
  • 如何像高级 JavaScript 开发人员一样为一般流程编写高阶函数
    英文|https://betterprogramming.pub/writing-high-order-functions-for-general-process-like-a-senior-javascript-developer-4d025baa3dc6翻译|杨小爱我们知道函数是......
  • JavaScript 文件上传完整指南
    https://betterprogramming.pub/a-complete-guide-of-file-uploading-in-javascript-2c29c61336f5翻译|杨小爱文件上传是Web项目的常用功能。相信大家在开发过程中或多......
  • JavaScript 中如何使用状态模式简化对象
    英文|https://medium.com/frontend-canteen/simplify-your-object-with-state-pattern-in-javascript-8674ff46edb1翻译|杨小爱状态模式是一个有趣的模式,它可能是解决一......
  • 10 个重构代码时的最佳实践
    什么是重构?重构是在不改变其功能的情况下改进现有代码设计的过程。作为软件开发人员,我们不断面临改进和优化代码的需求。无论是为了性能、可读性还是可维护性,重构代码都是一......
  • 11 个 ES2022(ES13)中惊人的 JavaScript 新特性
    英文|https://javascript.plainenglish.io/es13-javascript-features-eed7ed2f1497翻译|杨小爱与许多其他编程语言一样,JavaScript也在不断发展,每年,该语言都会通过新功......
  • 10 个CSS实现元素居中的方法汇总
    英文|https://javascript.plainenglish.io/10-css-tricks-you-should-know-for-centering-elements-61092d35b659翻译|杨小爱在前端开发工程师的日常生活中,使用CSS使......
  • 4 个编写JavaScript代码的关键原则
    英文|https://javascript.plainenglish.io/4-key-principles-to-writing-readable-and-efficient-javascript-code-1d022b685b3fJavaScript是一种非常灵活的编程语言,您可......