面试题Promise相关整理,深入理解Promise
引言
JavaScript 的 Promise 是处理异步操作的强大工具。理解 Promise 的工作原理和应用场景不仅对开发实际项目非常有用,同时也是技术面试中的常见考点。本文将系统性地整理 Promise 相关的面试题,提供详细的解答和实践示例,也是大厂面试中经常会遇到的,一起来看看你能答对几个吧。
问题1:Promise解决了什么问题?
解答: Promise是一种处理异步操作的对象。它解决了以下几个关键问题:
1)回调地狱:在使用回调函数处理异步操作时,代码容易陷入回调地狱。这会导致代码难以阅读和维护,因为每个异步操作都嵌套在上一个操作的回调函数中;
2)复杂的错误处理:在嵌套的回调中处理错误非常复杂,因为每个回调函数都需要独立地处理错误;
3)并行异步操作:在回调模式下并行执行多个异步操作并在所有操作完成后处理结果会变得非常复杂;
4)增强可读性和可维护性:嵌套的回调函数不仅难以阅读,而且难以调试和维护;
5)异步操作的控制反转:在回调模式中,异步操作的控制权在��步函数内部,不易进行更复杂的流程控制。
问题2:Promise,aysnc await,setTimeout三者的区别
解答: 三者都是处理异步操作的工具,但它们有不同的用法和适用场景。
-
语法差异:
Promise
:通过.then
、.catch
和.finally
方法链式调用,语法较为复杂。async/await
:基于Promise
,但以同步风格编写异步代码,语法更简洁、更具可读性。setTimeout
:用于延时执行代码,通常用于模拟异步行为。
-
错误处理不同:
Promise
:通过.catch
方法处理错误。async/await
:通过try...catch
块处理错误。setTimeout
:没有内置的错误处理机制,需要在回调函数内部处理错误。
-
应用场景不同:
Promise
:适用于多个异步操作的组合处理和链式调用。async/await
:适用于需要以同步风格编写异步代码的场景,特别是当有多个异步操作时。setTimeout
:用于延时执行代码,通常用于模拟异步行为或在某些操作后延时执行下一步。
-
执行机制不同:
Promise
:基于微任务队列(Microtask Queue),比宏任务(Macrotask)优先执行。async/await
:也是基于微任务队列,底层使用Promise
实现。setTimeout
:基于宏任务队列,延时后将回调放入宏任务队列中。
问题3:Promise.all和Promise.allsettled有什么区别?
在回答之前,我们先来总结一下Promise几个常用的函数功能:
-
Promise.all
:所有子任务都resolve才会返回自身的resolve,任意一个rejcet都会调用自身的rejcet。 -
Promise.allSettled
:所有子任务都回调(resolve或reject)才会调用自身resolve。(不会调用自身的reject) -
Promise.any
:任意一个子任务发生了resolve都会返回自身的resolve,所有子任务rejcet才会调用自身的rejcet。(与Promise.all相反) -
Promise.race
:任意一个子任务发生了回调(resolve或reject),都会立刻调用自身对应的回调。
解答:
1)功能差异
- Promise.all 将在子任务数组中的其中一个子任务失败后立即失败。
- Promise.allSettled 将永远不会失败,一旦数组中的所有子任务被完成或失败,它就会完成。
2)应用场景的差异
- 当多个请求之间有相互依赖,并且有一个promise reject时立即结束,通常使用Promise.all ()。
- 当您有多个彼此不依赖的异步任务成功完成时或者或者您总是想知道每个promise的结果时,通常使用Promise.allSettled ()。
问题4:手写实现一个Promise.all函数
实现思路:
首先遍历输入的 Promise 数组的所有子任务,返回一个新的Promise。跟踪每个 Promise 的状态,所有子任务都成功时解析为一个数组res,利用计数器判断是否结束,若都成功则返回resolve(res);若遇到异常,立刻返回reject(error)
示例:
function promiseAll(tasks) {
// 返回一个新的promise
return new Promise((resolve, reject) => {
if (!Array.isArray(tasks)) {
return reject(new TypeError('Argument must be an array'));
}
let resolvedCount = 0;
const res = new Array(tasks.length);
tasks.forEach((promise, index) => {
Promise.resolve(promise).then(
value => {
res[index] = value;
resolvedCount++;
if (resolvedCount === tasks.length) {
resolve(res);
}
},
error => {
reject(error);
}
);
});
});
}
// 示例使用
let promise1 = Promise.resolve(3);
let promise2 = 42;
let promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
promiseAll([promise1, promise2, promise3]).then(values => {
console.log(values); // 输出 [3, 42, "foo"]
}).catch(error => {
console.error(error);
});
问题5:手写实现一个Promise
实现思路:
首先,定义一个类如MyPromise,在构造函数中定义state、value、reason、onFulfilledCallbacks、onRejectedCallbacks,并且执行传递进来的函数,分别接收两个参数solve和reject,并实现这两个参数的处理函数_resolve和_reject;
其次,实现then函数。then函数需要返回一个新的promise,它接受两个参数,根据传入的参数,初始化好它们的值;然后根据状态state(pending、fullfilled、rejected),分别执行对应不同的逻辑;
最后,实现catch函数,直接调用then函数即可,传递一个onRejected函数作为参数。
示例:
class MyPromise {
constructor(executor) {
this.state = 'pending'; // 初始状态
this.value = undefined; // 操作成功的值
this.reason = undefined; // 操作失败的原因
this.onFulfilledCallbacks = []; // 成功回调
this.onRejectedCallbacks = []; // 失败回调
const _resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.onFulfilledCallbacks.forEach(callback => callback(this.value));
}
};
const _reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(callback => callback(this.reason));
}
};
try {
executor(_resolve, _reject);
} catch (err) {
_reject(err);
}
}
then(onFulfilled, onRejected) {
// 提供默认的回调函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
// 返回一个新的Promise,支持链式调用
return new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
try {
const x = onFulfilled(this.value);
resolve(x);
} catch (error) {
reject(error);
}
} else if (this.state === 'rejected') {
try {
const x = onRejected(this.reason);
resolve(x);
} catch (error) {
reject(error);
}
} else if (this.state === 'pending') {
this.onFulfilledCallbacks.push(() => {
try {
const x = onFulfilled(this.value);
resolve(x);
} catch (error) {
reject(error);
}
});
this.onRejectedCallbacks.push(() => {
try {
const x = onRejected(this.reason);
resolve(x);
} catch (error) {
reject(error);
}
});
}
});
}
catch(onRejected) {
return this.then(null, onRejected);
}
}
// 使用自定义的 MyPromise
let myPromise = new MyPromise((resolve, reject) => {
setTimeout(() => {
let success = true; // 模拟操作成功
if (success) {
resolve("Operation was successful!");
} else {
reject("Operation failed.");
}
}, 1000);
});
myPromise
.then((value) => `${value} 1`)
.then((value) => `${value} 2`)
.then((value) => `${value} 3`)
.then((value) => `${value} 4`)
.then((value) => {
console.log(value);
})
.catch((err) => {
console.error(err);
});
// 输出:Operation was successful! 1 2 3 4
转载自:https://juejin.cn/post/7399983901811589172