【前端面试小册】JS-第23节:面试官要的 Promise

一、Promise 基本原理

1.1 核心概念

Promise 是一个类,在执行这个类的时候会传入一个执行器,这个执行器会立即执行。

Promise 有三种状态

  • Pending(等待):初始状态
  • Fulfilled(完成):操作成功完成
  • Rejected(失败):操作失败

状态转换规则

  • PendingFulfilled
  • PendingRejected
  • 一旦发生改变便不可二次修改

状态转换图

graph TD
    A[Pending 等待] --> B[Fulfilled 完成]
    A --> C[Rejected 失败]
    B --> D[状态锁定]
    C --> D

1.2 面试官想要什么样的 Promise.then

通常考得最多的就是 Promise.then,而这里能让面试官一亮的 then 要支持:

  1. 异步函数的处理:能够正确处理异步回调
  2. 支持链式调用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);
                        }
                    }
                );
            });
        });
    }
}

八、面试要点总结

核心知识点

  1. Promise 三种状态:Pending、Fulfilled、Rejected
  2. 状态转换:只能从 Pending 转换,且不可逆
  3. then 方法:支持异步处理和链式调用
  4. 静态方法: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-12 20:21
已编辑
电子科技大学 Java
牛丫丫丫:看这个投票太扯了,要是真这么多人报的37K以上,hr就不会一再的降低base了,肯定是一堆人报低了给hr错觉了
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务