什么是Promise
在 JavaScript 中,Promise 是用来处理异步操作的一种方法。
- Promise 的本质是一个容器,用来保存在未来某个时间才会完成的操作的结果。
- Promise 允许以一种更清晰、可维护的方式来处理异步操作,避免了回调地狱(Callback)的问题。
Promise的三种状态
- Pending(待定):初始状态,Promise 还没有被处理。
- Resolved/Fulfilled(已完成):操作成功完成,Promise被解决(通常会返回结果)。
- Rejected(已拒绝):操作失败,Promise 被拒绝(通常会返回错误)。
Promise 的基本用法
创建一个Promise
通过 new Promise 创建一个新的 Promise 对象。Promise 构造函数接收一个函数,这个函数包含两个参数:resolve 和 reject
const promise = new Promise((resolve, reject) => {
const success = true; // 模拟异步操作结果
if(success) {
resolve('操作成功')
} else {
reject('操作失败')
}
})
使用 then() 和 catch()
Promise 提供了 then()和catch()方法来处理异步操作的结果。then()用于处理成功的情况,而catch()用于处理失败的情况。
promise.then(result => {
console.log(result) // 输出“操作成功”
}).catch(error => {
console.log(error) // 输出“操作失败”
})
链式调用
then() 和catch()可以链式调用,这使得异步操作可以依次执行,结果依次传递。
promise.then(result => {
console.log(result) // 输出 “操作成功”
return '下一个操作'
}).then(nextResult => {
console.log(nextResult) // 下一个操作
}).catch(error => {
console.log(error)
})
Promise 的常见用法
Promise.all()
Promise.all()方法接收一个包含多个Promise的数组,并返回一个新的 Promise。只有当所有 Promise 都成功时,Promise.all()才会成功,否则只要有一个失败,它就会立即失效。
const promise1 = Promise.resolve('结果1');
const promise2 = Promise.resolve('结果2');
const promise3 = Promise.resolve('结果3');
Promise.all([promise1, promise2, promise3]).then(results => {
console.log(results) // ['结果1', '结果2', '结果3']
}).catch(error => {
console.log(error)
})
Promise.race()
Promise.race()方法接收一个包含多个 Promise 的数组,返回一个新的 Promise。这个新 Promise 的结果是第一个完成的 Promise 的结果,无论它是成功还是失败。
const promise1 = new Promise((resolve, reject) => setTimeout(resolve, 100, '结果1'))
const promise2 = new Promise((resolve, reject) => setTimeout(resolve, 200, '结果2'))
Promise.race([promise1, promise2]).then(result => console.log(result)) // 输出结果1,因为它更快完成
-
语法解析 - setTimeout(resolve, 100, '结果1')
setTimeout(function, delay, arg1, arg2, ..., argN);
- function:要执行的回调函数,在这里相当于 resolve
- delay:延迟时间,在这里是传入的 100
- arg:传递给回调函数的参数,在这里是传入的 '结果1'
Promise.allSettled()
Promise.allSettled()会等待所有 Promise 都处理完,无论他们是成功还是失败。它返回一个包含每个Promise状态及其结果的数组。
const promise1 = Promise.resolve('成功')
const promise2 = Promise.reject('失败')
Promise.allSettled([promise1, promise2]).then(results => {
console.log(results)
// [{status: 'fulfilled', value: '成功'}, {status: 'rejected', reason: '失败'} ]
})
Promise.any()
Promise.any()返回第一个成功的Promise,若所有Promise都失败,则返回失败。
const promise1 = Promise.reject('失败1')
const promise2 = Promise.resolve('成功')
const promise3 = Promise.reject('失败2')
Promise.any([promise1, promise2, promise3]).then(result => {console.log(result)}) // 成功
Promise 的 异步实现
理解 Promise 的执行机制很重要,特别是其内部如何处理异步操作
Promise 执行顺序
JavaScript 中的异步执行通常基于事件循环机制(Event Loop)。Promise 的执行顺序比setTimeout等宏任务更优先。即使在 then 里没有回调,Promise 的状态变化也是异步的。
console.log("开始")
const promise = new Promise((resolve, reject) => {
resolve('Promise 执行')
})
promise.then(result => {
console.log(result)
})
console.log("结束")
/**
输出的顺序是
开始
结束
Promise 执行
*/
Promise 内部实现 - 手撕Promise
Js 版本
class MyPromise {
constructor(target){
this.status = 'pending'; // 初始状态
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if(this.status === 'pending') {
this.status = 'fulfilled';
this.value = value;
this.onResolvedCallbacks.forEach(fn => fn())
}
}
const reject = (reason) => {
if(this.status === 'pending') {
this.status = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn())
}
}
target(resolve, reject)
}
then(onFulfilled, onRejected) {
if(this.status === 'fulfilled') {
onFulfilled(this.value)
} else if(this.status === 'rejected') {
onRejected(this.reason)
} else {
this.onResolvedCallbacks.push(() => {
onFulfilled(this.value)
})
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
}
}
}
TS 版本
// 定义 Promise 状态的类型
type PromiseState = 'pending' | 'fulfilled' | 'rejected';
/**
* 定义执行器函数的类型
* @template T 代表 Promise 解析后值的类型
* @param resolve 解析 Promise 的函数,接收一个值或者另一个 Promise
* @param reject 拒绝 Promise 的函数,接收一个错误原因
*/
type Executor<T> = (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void;
/**
* then 方法中的回调类型
* @template T 代表前一个 Promise 解析后的值的类型
*/
type ResolveCallback<T> = (value: T) => void;
type RejectCallback = (reason?: any) => void;
class MyPromise<T> {
private state: PromiseState = 'pending'; // 当前Promise的状态
private value: T | undefined = undefined; // 存储 Promise 解析后的值
private reason: any = undefined; // 存储 Promise 的拒绝原因
private onResolvedCallbacks: ResolveCallback<T>[] = []; // 存储 resolve 的回调
private onRejectedCallbacks: RejectCallback[] = []; // 存储 reject 的回调
/**
* Promise 构造函数
* @param executor 传入的执行器函数,接收 resolve 和 reject
*/
construct(executor: Executor<T>) {
// 定义 resolve 方法,状态由 pending => fulfilled
const resolve: ResolveCallback<T> = (value: T | PromiseLike<T>) => {
if(this.state === 'pending') {
this.state = 'fulfilled';
this.value = value as T;
// 执行所有 resolve 的回调
this.onResolvedCallbacks.forEach(fn => fn(this.value as T))
}
}
// 定义 reject 方法,状态由 pending => rejected
const Reject: RejectCallback = (reason?: any) => {
if(this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
// 执行所有 reject 的回调
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}
}
try {
executor(resolve, reject); // 执行传入的执行器函数
} catch (error) {
reject(error)
}
}
/**
* then 方法,返回一个新的 Promise
* @template U then之后返回新的 Promise 的值的类型
* @param onFulfilled 处理成功的回调
* @param onRejected 处理失败的回调
* @returns 一个新的 Promise 实例
*/
then<U>(
onFulfilled?: ResolveCallback<T> | undefined,
onRejected?: RejectCallback | undefined
): MyPromise<U>{
return new MyPromise<U>((resolve, reject) => {
// 当 Promise 状态是 fulfilled时,调用 onFulfilled
if(this.state === 'fulfilled') {
if(onFulfilled){
try{
const result = onFulfilled(this.value as T);
resolve(result)
} catch(error) {
reject(error)
}
} else {
resolve(this.value as T); // 没有传递 onFulfilled 时直接 resolve
}
}
// 当 Promise 状态是 rejected 时,调用 onRejected
if(this.state === 'rejected') {
if(onRejected) {
try {
const result = onRejected(this.reason);
resolve(result)
} catch (error) {
reject(error)
}
} else {
reject(this.reason) // 没有传递 onRejected 时直接 reject
}
}
// 如果 Promise 还处于 pending 状态,将回调函数推入队列
if(this.state === 'pending') {
if(onFulfilled){
this.onResolvedCallbacks.push(() => {
try {
const result = onFulfilled(this.value as T)
resolve(result as U);
} catch (error) {
reject(error)
}
})
}
if(onRejected) {
this.onRejectedCallbacks.push(() => {
try {
const result = onRejected(this.reason)
resolve(result as U)
} catch (error) {
reject(error)
}
})
}
}
})
}
}
PromiseLike 是什么
PromiseLike<T>是 TypeScript 内类的一个类型,它用于表示一个“类Promise 对象”(即一个符合then方法规范的对象)
- 作用
PromiseLike<T>代表任何拥有then方法的对象,而不一定是Promise<T>本身- 它允许 与Promise兼容的对象作为
resolve()的参数,这样就可以实现链式调用。
小扩展
由于 MyPromise是一个通用类型,可以传递不同的泛型参数,链式调用的返回类型会自动推断。若返回值是一个 Promise ,会继续等待其解析。
new MyPromise<number>((resolve, reject) => resolve(25))
.then(result => {
console.log(result) // 42
return new MyPromise<string>((resolve) => resolve('链式结果'))
})
.then(finalResult => {
console.log(finalResult) // '链式结果'
})
继续撕Promise.all()
function myPromiseAll<T>(promises: Array<T | Promise<T>>):Promise<T[]>{
return new Promise<T[]>((resolve, reject) => {
const results: T[] = [];
let count = 0;
const total = promises.length;
if(total === 0) {
resolve([]) // 传入空数组时,立即返回 Promise<[]>
return
}
Promise.forEach((promise, index) => {
Promise.resolve(Promise) // 确保普通值也能兼容
.then(value => {
results[index] = value; // 保存当前 promise 的结果
count++
if(count === total) {
resolve(results) // 所有 promise 成功后,resolve 结果数组
}
})
.catch(reject) // 只要有一个失败,立即 reject 整个 Promise.all
})
})
}
实现核心
- 维护一个 count 变量,确保所有
Promise解析后才resolve - 失败时立即
reject(),不会等待其他Promise - 使用
Promise.resolve()统一处理普通值和Promise
再撕Promise.race()
function MyPromiseRace<T>(promises: Array<T } Promise<T>>): Promise<T> {
return new Promise<T>((resolve, reject) => {
for(const promise of promises) {
Promise.resolve(promise).then(resolve, reject)
}
})
}
追问:then(resolve, reject) 和 then(resolve).catch(reject) 有什么区别
- 结果不会改变,因为两者在逻辑上是等价的
- 唯一的不同点:
then(resolve, rejecr)会吞掉一些throw的异常,而then(resolve).catch(reject)不会吞掉异常
Promise 的高级技巧
Promise 的性能优化
可以使用 Promise.all()来并行执行多个依赖操作,从而提高性能。但是,多个Promise的顺序执行会影响性能,特别是当有依赖关系时,这时候就需要使用async/await来逐步处理异步操作
异步错误处理
通过catch()或try/catch来捕获异步操作的错误非常重要的。即使 Promise 被拒绝了,错误也应当被适当的处理
async function example(){
try{
const result = await someAsyncFunction();
console.log(result)
} catch (error) {
console.error(error)
}
}