“少年,我是Promise,听说你要手撕我?”

398 阅读6分钟

什么是Promise

在 JavaScript 中,Promise 是用来处理异步操作的一种方法。

  • Promise 的本质是一个容器,用来保存在未来某个时间才会完成的操作的结果。
  • Promise 允许以一种更清晰、可维护的方式来处理异步操作,避免了回调地狱(Callback)的问题。
Promise的三种状态
  • Pending(待定):初始状态,Promise 还没有被处理。
  • Resolved/Fulfilled(已完成):操作成功完成,Promise被解决(通常会返回结果)。
  • Rejected(已拒绝):操作失败,Promise 被拒绝(通常会返回错误)。

Promise 的基本用法

创建一个Promise

通过 new Promise 创建一个新的 Promise 对象。Promise 构造函数接收一个函数,这个函数包含两个参数:resolvereject

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