【前端面试小册】JS-第23节:面试官要的 Promise
一、Promise 基本原理
1.1 核心概念
Promise 是一个类,在执行这个类的时候会传入一个执行器,这个执行器会立即执行。
Promise 有三种状态:
- Pending(等待):初始状态
- Fulfilled(完成):操作成功完成
- Rejected(失败):操作失败
状态转换规则:
Pending→FulfilledPending→Rejected- 一旦发生改变便不可二次修改
状态转换图:
graph TD
A[Pending 等待] --> B[Fulfilled 完成]
A --> C[Rejected 失败]
B --> D[状态锁定]
C --> D
1.2 面试官想要什么样的 Promise.then
通常考得最多的就是 Promise.then,而这里能让面试官一亮的 then 要支持:
- 异步函数的处理:能够正确处理异步回调
- 支持链式调用:
then方法返回新的 Promise,支持链式调用
二、Promise.then 实现
2.1 需求示例
示例 1:支持异步回调
// 支持异步回调
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('愚公上岸说');
}, 2000);
});
promise.then(value => {
console.log('resolve', value);
});
// 要能输出:resolve 愚公上岸说
示例 2:支持链式调用
// 支持链式调用
const promise = new MyPromise((resolve, reject) => {
resolve('愚公上岸说');
});
function demo() {
return new MyPromise((resolve, reject) => {
resolve('巴菲特');
});
}
promise
.then(value => {
console.log(1);
console.log('resolve', value);
return demo();
})
.then(value => {
console.log(2);
console.log('resolve', value);
});
// 希望输出结果如下:
// 1
// resolve 愚公上岸说
// 2
// resolve 巴菲特
2.2 完整实现
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 完成
const REJECTED = 'rejected'; // 失败
class MyPromise {
constructor(executor) {
this.value = null;
this.error = null;
this.state = PENDING;
this.fulfilledQueue = []; // 成功任务队列,存异步任务
this.rejectQueue = []; // 失败任务队列,存异步任务
const resolve = value => {
if (this.state === PENDING) {
// 执行 resolve 的时候改变 state 的状态为完成状态
this.state = FULFILLED;
// 将 resolve 的值存起来
this.value = value;
// 遍历异步任务并执行
this.fulfilledQueue.forEach(fn => {
fn(this.value);
});
}
};
const reject = error => {
if (this.state === PENDING) {
this.state = REJECTED;
// 保存失败原因
this.error = error;
// 执行异步任务
this.rejectQueue.forEach(fn => {
fn(this.error);
});
}
};
try {
// 将 resolve 和 reject 函数传递给 executor
// 回忆 Promise 使用:new Promise(fn),函数 fn 可以获得 resolve,reject
// 即:new Promise((resolve, reject) => { resolve })
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
// 参数默认值处理
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error; };
const nextPromise = new MyPromise((resolve, reject) => {
if (this.state === FULFILLED) {
// 异步执行,确保 then 回调在微任务中执行
setTimeout(() => {
try {
// 获取成功回调函数的执行结果
const res = onFulfilled(this.value);
// 判断 res 是否是 MyPromise 的实例对象
if (res instanceof MyPromise) {
// 执行 res 的 then 方法,目的将其状态变成完成或者拒绝
res.then(resolve, reject);
} else {
// 普通值,非 Promise
resolve(res);
}
} catch (error) {
reject(error);
}
}, 0);
} else if (this.state === REJECTED) {
// 异步执行
setTimeout(() => {
try {
const res = onRejected(this.error);
if (res instanceof MyPromise) {
res.then(resolve, reject);
} else {
resolve(res);
}
} catch (error) {
reject(error);
}
}, 0);
} else {
// 执行到这一步说明当前 state 的状态不是完成态,也说明传给 Promise 的函数还没有 resolve
// 所以先将成功和失败的回调函数,存入任务入队列
this.fulfilledQueue.push((value) => {
setTimeout(() => {
try {
const res = onFulfilled(value);
if (res instanceof MyPromise) {
res.then(resolve, reject);
} else {
resolve(res);
}
} catch (error) {
reject(error);
}
}, 0);
});
this.rejectQueue.push((error) => {
setTimeout(() => {
try {
const res = onRejected(error);
if (res instanceof MyPromise) {
res.then(resolve, reject);
} else {
resolve(res);
}
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return nextPromise;
}
catch(onReject) {
return this.then(null, onReject);
}
}
2.3 关键点解析
2.3.1 状态管理
this.state = PENDING;
this.fulfilledQueue = [];
this.rejectQueue = [];
作用:
- 维护 Promise 的当前状态
- 存储待执行的异步回调
2.3.2 异步处理
setTimeout(() => {
// 执行回调
}, 0);
原因:
- 确保
then回调在微任务中执行 - 符合 Promise A+ 规范
2.3.3 链式调用
const nextPromise = new MyPromise((resolve, reject) => {
// ...
});
return nextPromise;
原理:
then返回新的 Promise- 支持链式调用
2.3.4 Promise 返回值处理
if (res instanceof MyPromise) {
res.then(resolve, reject);
} else {
resolve(res);
}
逻辑:
- 如果返回 Promise,等待其完成
- 如果返回普通值,直接 resolve
2.4 测试用例
异步回调测试
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('异步回调');
}, 2000);
});
promise.then(value => {
console.log('resolve', value);
});
// 输出:resolve 异步回调
链式调用测试
const promise = new MyPromise((resolve, reject) => {
resolve('愚公上岸说');
});
function demo() {
return new MyPromise((resolve, reject) => {
resolve('巴菲特');
});
}
promise
.then(value => {
console.log(1);
console.log('resolve', value);
return demo();
})
.then(value => {
console.log(2);
console.log('resolve', value);
});
// 输出:
// 1
// resolve 愚公上岸说
// 2
// resolve 巴菲特
三、Promise.all 实现
3.1 功能说明
Promise.all 接收一个 Promise 数组,返回一个新的 Promise:
- 当所有 Promise 都成功时,返回结果数组
- 当任意一个 Promise 失败时,立即返回失败
3.2 实现代码
static all(promiseList) {
if (!Array.isArray(promiseList)) {
throw new Error('必须是数组');
}
const len = promiseList.length;
// 生成一个长度等于任务队列的空数组,用来保存任务结果
const resultQueue = new Array(promiseList.length);
// 计数器
let count = 0;
return new MyPromise((resolve, reject) => {
// 如果数组为空,直接返回
if (len === 0) {
resolve([]);
return;
}
for (const [idx, item] of promiseList.entries()) {
// 使用 Promise.resolve 包装,确保是 Promise
MyPromise.resolve(item).then(
res => {
// 每个任务都按照他遍历时的顺序去存值,就能保证和输出结果的顺序和输入顺序一致
resultQueue[idx] = res;
// 计数器加 1
count++;
// 计速次数等于任务长度,返回结果
if (count === len) {
resolve(resultQueue);
}
},
err => {
// 任意一个失败,立即 reject
reject(err);
}
);
}
});
}
3.3 测试用例
const p1 = new MyPromise(function(resolve) {
setTimeout(function() {
resolve(1);
}, 1000);
});
const p2 = new MyPromise(function(resolve) {
setTimeout(function() {
resolve(2);
}, 2000);
});
const p3 = new MyPromise(function(resolve) {
setTimeout(function() {
resolve(3);
}, 3000);
});
MyPromise.all([p3, p1, p2, 4]).then(res => {
console.log(res); // [ 3, 1, 2, 4 ]
});
四、Promise.race 实现
4.1 功能说明
Promise.race 接收一个 Promise 数组,返回一个新的 Promise:
- 当任意一个 Promise 完成(成功或失败)时,立即返回该 Promise 的结果
4.2 实现代码
static race(promiseList) {
if (!Array.isArray(promiseList)) {
throw new Error('必须是数组');
}
// race 返回的也是 Promise
return new MyPromise((resolve, reject) => {
if (promiseList.length === 0) {
return;
}
for (const item of promiseList) {
// 使用 Promise.resolve 包装
MyPromise.resolve(item).then(
res => {
resolve(res);
},
err => {
reject(err);
}
);
}
});
}
4.3 测试用例
const p1 = new MyPromise((resolve) => {
setTimeout(() => resolve(1), 1000);
});
const p2 = new MyPromise((resolve) => {
setTimeout(() => resolve(2), 500);
});
MyPromise.race([p1, p2]).then(res => {
console.log(res); // 2(p2 先完成)
});
五、Promise.allSettled 实现
5.1 功能说明
Promise.allSettled 接收一个 Promise 数组,返回一个新的 Promise:
- 等待所有 Promise 完成(无论成功或失败)
- 返回结果数组,包含每个 Promise 的状态和值
5.2 实现代码
static allSettled(promiseList) {
if (!Array.isArray(promiseList)) {
throw new Error('必须是数组');
}
return new MyPromise(function(resolve) {
const results = [];
let resolvedCount = 0;
const count = promiseList.length;
if (count === 0) {
resolve([]);
return;
}
promiseList.forEach((curPro, index) => {
MyPromise.resolve(curPro).then(
(data) => {
resolvedCount++;
results[index] = {
status: "fulfilled",
value: data,
};
if (resolvedCount >= count) {
resolve(results);
}
},
(reason) => {
resolvedCount++;
results[index] = {
status: "rejected",
reason,
};
if (resolvedCount >= count) {
resolve(results);
}
}
);
});
});
}
六、Promise.resolve 和 Promise.reject
6.1 Promise.resolve 实现
static resolve(val) {
if (val instanceof MyPromise) {
return val;
}
return new MyPromise(res => res(val));
}
6.2 Promise.reject 实现
static reject(val) {
return new MyPromise((res, rej) => rej(val));
}
七、完整的 Promise 实现
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
this.value = null;
this.error = null;
this.state = PENDING;
this.fulfilledQueue = [];
this.rejectQueue = [];
const resolve = value => {
if (this.state === PENDING) {
this.state = FULFILLED;
this.value = value;
this.fulfilledQueue.forEach(fn => fn(this.value));
}
};
const reject = error => {
if (this.state === PENDING) {
this.state = REJECTED;
this.error = error;
this.rejectQueue.forEach(fn => fn(this.error));
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error; };
const nextPromise = new MyPromise((resolve, reject) => {
const handleFulfilled = (value) => {
setTimeout(() => {
try {
const res = onFulfilled(value);
if (res instanceof MyPromise) {
res.then(resolve, reject);
} else {
resolve(res);
}
} catch (error) {
reject(error);
}
}, 0);
};
const handleRejected = (error) => {
setTimeout(() => {
try {
const res = onRejected(error);
if (res instanceof MyPromise) {
res.then(resolve, reject);
} else {
resolve(res);
}
} catch (error) {
reject(error);
}
}, 0);
};
if (this.state === FULFILLED) {
handleFulfilled(this.value);
} else if (this.state === REJECTED) {
handleRejected(this.error);
} else {
this.fulfilledQueue.push(handleFulfilled);
this.rejectQueue.push(handleRejected);
}
});
return nextPromise;
}
catch(onReject) {
return this.then(null, onReject);
}
static resolve(val) {
if (val instanceof MyPromise) {
return val;
}
return new MyPromise(res => res(val));
}
static reject(val) {
return new MyPromise((res, rej) => rej(val));
}
static all(promiseList) {
if (!Array.isArray(promiseList)) {
throw new Error('必须是数组');
}
const len = promiseList.length;
const resultQueue = new Array(len);
let count = 0;
return new MyPromise((resolve, reject) => {
if (len === 0) {
resolve([]);
return;
}
for (const [idx, item] of promiseList.entries()) {
MyPromise.resolve(item).then(
res => {
resultQueue[idx] = res;
count++;
if (count === len) {
resolve(resultQueue);
}
},
err => reject(err)
);
}
});
}
static race(promiseList) {
if (!Array.isArray(promiseList)) {
throw new Error('必须是数组');
}
return new MyPromise((resolve, reject) => {
if (promiseList.length === 0) {
return;
}
for (const item of promiseList) {
MyPromise.resolve(item).then(resolve, reject);
}
});
}
static allSettled(promiseList) {
if (!Array.isArray(promiseList)) {
throw new Error('必须是数组');
}
return new MyPromise((resolve) => {
const results = [];
let resolvedCount = 0;
const count = promiseList.length;
if (count === 0) {
resolve([]);
return;
}
promiseList.forEach((curPro, index) => {
MyPromise.resolve(curPro).then(
(data) => {
resolvedCount++;
results[index] = { status: "fulfilled", value: data };
if (resolvedCount >= count) {
resolve(results);
}
},
(reason) => {
resolvedCount++;
results[index] = { status: "rejected", reason };
if (resolvedCount >= count) {
resolve(results);
}
}
);
});
});
}
}
八、面试要点总结
核心知识点
- Promise 三种状态:Pending、Fulfilled、Rejected
- 状态转换:只能从 Pending 转换,且不可逆
- then 方法:支持异步处理和链式调用
- 静态方法:all、race、allSettled、resolve、reject
常见面试题
Q1: Promise.then 如何支持链式调用?
答:then 方法返回新的 Promise,新 Promise 的状态由回调函数的返回值决定。如果返回 Promise,等待其完成;如果返回普通值,直接 resolve。
Q2: Promise.all 和 Promise.race 的区别?
答:
Promise.all:等待所有 Promise 成功,任意一个失败立即失败Promise.race:任意一个 Promise 完成(成功或失败)立即返回
Q3: 如何实现 Promise.allSettled?
答:等待所有 Promise 完成(无论成功或失败),返回包含每个 Promise 状态和值的结果数组。
实战建议
- ✅ 理解 Promise 的状态机制
- ✅ 掌握 then 的链式调用原理
- ✅ 理解异步处理的实现方式
- ✅ 能够手写实现 Promise 的核心方法
前端面试小册 文章被收录于专栏
每天更新3-4节,持续更新中... 目标:50天学完,上岸银行总行!
查看11道真题和解析