Promise及使用场景

53 阅读6分钟

Promise是ES6提出的对于异步编程的解决方案,对回调函数进行包装,可以解决异步编程回调地狱的问题,降低了编码难度且代码可读性更高。

Promise是什么

Promise意为承诺,承诺一个异步操作会在将来兑现。本质上是一个对象,这个对象有三种状态,分别是pending(进行中),fulfilled(成功)和rejected(失败)。Promise在合适的时机触发相应状态的回调来达到处理异步的目的。
Promise实例被声明时会立即执行,中途无法取消,状态确定下来后也无法再更改。
Promise的构造函数接收一个函数,该函数有两个参数(resolve和reject),分别是成功和失败要执行的操作,如果执行resolve,状态就会从pending变成fulfilled;如果执行reject,状态就会从pending变成rejected。
Promise实例有三个方法,分别是then()、catch()、finally(),我们可以在其中定义成功状态、失败状态和最终要触发的回调函数,这些回调函数的参数就是前面用resolve或rejected抛出的参数。而且then和catch方法会返回一个新的promise对象,这个对象也有自己的状态,可以定义相应的回调函数,而这些回调函数又会返回promise对象......就可以这样无限套娃下去,直到遇到finally执行最终的操作。写起来是链式的,执行起来是嵌套的。
ps:其实then可以接收两个回调,第二个是reject回调,但我们一般不会这么写,而是把reject的回调放在catch里

// deepseek写的演示代码
const promise = new Promise((resolve) => {
    resolve("操作成功"); // 直接触发成功状态
})
.catch((error) => {
    console.log("这行会被跳过[0] catch:", error);
})
.then((result) => {
    console.log("[1] then:", result); // 接收成功结果
    return "加工后的数据"; // 传递新值给下一个 then
})
.then((processedData) => {
    console.log("[2] then:", processedData); // 处理加工后的数据
    throw new Error("模拟中途错误"); // 主动抛出错误
})
.catch((error) => {
    console.log("[3] catch:", error.message); // 捕获错误
    return "错误恢复后的数据"; // 返回新值继续链式调用
})
.finally(() => {
    console.log("[4] finally: 清理资源"); // 无论成功失败都会执行
})

// [1] then: 操作成功
// [2] then: 加工后的数据
// [3] catch: 模拟中途错误
// [4] finally: 清理资源

Promise的静态方法

回顾promise常用的静态方法并手写一下

promise.all

用于将一个promise实例数组(可迭代对象)包装成一个新的promise实例
只有数组中所有promise状态都是fulfilled,新promise状态才是fulfilled,且将所有promise的返回值组成数组,传给新promise的回调函数;否则只要有一个rejected,新promise的状态就变成rejected,且第一个rejected的promise的返回值传给新promise的回调,特别的,如果rejected的promise有自己的catch,就执行自己的catch,不触发新promise的catch。

const p1 = new Promise((res, rej) => res('p1成功'))
const p2 = new Promise((res, rej) => res('p2成功'))
const p3 = new Promise((res, rej) => rej('p3失败'))
const p4 = new Promise((res, rej) => rej('p4失败'))

Promise.all([p1, p2]).then(res => {
  console.log(res) // [ 'p1成功', 'p2成功' ]
}).catch(err => {
  console.log(err)
})
Promise.all([p1, p2, p3, p4]).then(res => {
  console.log(res)
}).catch(err => {
  console.log(err) // p3失败
})

promise.all的实现思路是怎样的呢?我们来模拟一下

  • 返回值是一个新的promise
  • 先把可迭代对象转换为数组,并开一个新数组用于返回fulfilled结果
  • 遍历数组中的每个promise,依次把它们resolve转换成promise值,如果正常返回,就把返回值存到结果数组中,如果碰到一个错误,就立即抛出并rejected
  • 在遍历数组时实时更新未处理的promise的数量,如果已经处理完所有promise,才resolve
type Fn<T> = () => Promise<T>

function promiseAll<T>(functions: Fn<T>[]): Promise<T[]> {
    return new Promise((resolve, reject) => {
        if(functions.length === 0) resolve([])
        let result = new Array(functions.length)
        let count = 0
        // functions.forEach((fn, idx) => {
        //     fn().then(res => {
        //         result[idx] = res
        //         count++
        //         if(count === functions.length) return resolve(result)
        //     }).catch(rej => reject(rej))
        // })
        functions.forEach(async (fn, idx) => {
            try{
                const res = await fn()
                result[idx] = res
                count++
                if(count === functions.length) resolve(result)
            } catch(err){
                reject(err)
            }
        })
    })
};

通过all可以将多个请求合并在一起,汇总所有请求的结果,只需要一个loading即可

promise.race

和promise.all类似,不过新promise的状态取决于第一个状态发生改变的promise,并把这个promise的返回值传给新promise的回调。如果超过规定时间仍没有promise状态发生改变,则新promise的状态为rejected。根据这个特性,race可以用于设置图片请求超时
功能演示如下:

// 第一个完成的 Promise 是成功的
Promise.race([
    new Promise(resolve => setTimeout(() => resolve(1), 2000)),
    new Promise(resolve => setTimeout(() => resolve(2), 1000)),
]).then(console.log); // 输出(1秒后): 2
  
// 第一个完成的 Promise 是失败的
Promise.race([
    new Promise((_, reject) => setTimeout(() => reject("Error"), 1000)),
    new Promise(resolve => setTimeout(() => resolve(2), 2000)),
]).catch(console.error); // 输出(1秒后): Error

race手写起来就比all简单很多了,只需要返回第一个做出响应的promise即可

Promise.myRace = function(promises) {
    return new Promise((resolve, reject) => {
        try {
            // 1. 将可迭代对象转换为数组(若不可迭代,此处会抛错)
            const iterable = Array.from(promises);

            // 2. 遍历每个 Promise
            iterable.forEach((item) => {
                // 3. 将元素转换为 Promise(兼容非 Promise 值)
                Promise.resolve(item) // 4. 哪个先完成就返回哪个
                    .then(resolve)
                    .catch(reject);
            });
        } catch (error) {
            // 参数不可迭代时拒绝
            reject(error);
        }
    });
}

promise.allSettled

和上面的方法参数一样,不过是要等到所有promise都返回结果,不管是成功还是失败,新promise的状态永远是成功。把所有promise的结果存在数组里传给新promise的回调。就不手写了

promise.resolve和promise.reject

将现有对象转换成promise实例,状态为resolved/rejected

手写promise

class MyPromise {
    constructor(executor) {
      this.state = 'pending'; // 初始状态
      this.value = undefined; // 成功值
      this.reason = undefined; // 失败原因
      this.onFulfilledCallbacks = []; // 成功回调队列
      this.onRejectedCallbacks = []; // 失败回调队列
  
      // 定义 resolve 函数
      const resolve = (value) => {
        if (this.state === 'pending') {
          this.state = 'fulfilled';
          this.value = value;
          // 异步触发回调
          this.onFulfilledCallbacks.forEach(fn => fn());
        }
      };
  
      // 定义 reject 函数
      const reject = (reason) => {
        if (this.state === 'pending') {
          this.state = 'rejected';
          this.reason = reason;
          // 异步触发回调
          this.onRejectedCallbacks.forEach(fn => fn());
        }
      };
  
      // 执行器立即执行,捕获错误
      try {
        executor(resolve, reject);
      } catch (error) {
        reject(error);
      }
    }
  
    // 实现 then 方法
    then(onFulfilled, onRejected) {
      // 处理值穿透(例如 then() 不传回调)
      onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
      onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };
  
      // 返回新 Promise 以实现链式调用
      const promise2 = new MyPromise((resolve, reject) => {
        // 统一处理回调
        const handleCallback = (callback, value) => {
          setTimeout(() => { // 模拟异步
            try {
              const result = callback(value);
              // 处理返回 Promise 的情况
              if (result instanceof MyPromise) {
                result.then(resolve, reject);
              } else {
                resolve(result);
              }
            } catch (error) {
              reject(error);
            }
          }, 0);
        };
  
        // 当前 Promise 已完成
        if (this.state === 'fulfilled') {
          handleCallback(onFulfilled, this.value);
        } 
        // 当前 Promise 已失败
        else if (this.state === 'rejected') {
          handleCallback(onRejected, this.reason);
        } 
        // 当前 Promise 未完成,存储回调
        else {
          this.onFulfilledCallbacks.push(() => handleCallback(onFulfilled, this.value));
          this.onRejectedCallbacks.push(() => handleCallback(onRejected, this.reason));
        }
      });
  
      return promise2;
    }
  
    // 实现 catch 方法
    catch(onRejected) {
      return this.then(null, onRejected);
    }
  
    // 静态 resolve 方法
    static resolve(value) {
      return new MyPromise(resolve => resolve(value));
    }
  
    // 静态 reject 方法
    static reject(reason) {
      return new MyPromise((_, reject) => reject(reason));
    }
}
// 测试链式调用
const p = new MyPromise((resolve) => {
    setTimeout(() => resolve('成功'), 1000); // 模拟异步
});
  
p.then((value) => {
    console.log('第一次 then:', value); // 1秒后输出: 第一次 then: 成功
    return MyPromise.resolve('新 Promise');
})
.then((value) => {
    console.log('第二次 then:', value); // 输出: 第二次 then: 新 Promise
    throw new Error('测试错误');
})
.catch((error) => {
    console.log('捕获错误:', error.message); // 输出: 捕获错误: 测试错误
});