Promise及使用场景

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

Promise是什么

Promise意为承诺,承诺一个异步操作会在将来兑现。本质上是一个对象,这个对象有三种状态,分别是pending(进行中),fulfilled(成功)和rejected(失败)。 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里

js 复制代码
// 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。

js 复制代码
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
js 复制代码
Promise.myAll = function(promises) {
    return new Promise((resolve, reject) => {
        try {
        // 1. 将可迭代对象转换为数组(若不可迭代,此处会抛错)
        const iterable = Array.from(promises);
        
        // 2. 空数组直接成功
        if (iterable.length === 0) {
            resolve([]);
            return;
        }

        const results = new Array(iterable.length); // 按顺序保存结果
        let remaining = iterable.length; // 未完成的 Promise 数量

        // 3. 遍历每个 Promise
        iterable.forEach((item, index) => {
            // 4. 将元素转换为 Promise(兼容非 Promise 值)
            Promise.resolve(item)
            .then((value) => {
                results[index] = value; // 按索引保存结果
                remaining--;
                // 5. 全部成功时解析结果
                if (remaining === 0) resolve(results);
            })
            .catch(reject); // 6. 任意失败立即拒绝
        });
        } catch (error) {
            // 参数不可迭代时拒绝
            reject(error);
        }
    });
}

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

promise.race

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

功能演示如下:

js 复制代码
// 第一个完成的 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即可

js 复制代码
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

js 复制代码
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));
    }
}
js 复制代码
// 测试链式调用
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); // 输出: 捕获错误: 测试错误
});
相关推荐
好_快2 分钟前
Lodash源码阅读-takeWhile
前端·javascript·源码阅读
恋猫de小郭1 小时前
Android Studio Cloud 正式上线,不只是 Android,随时随地改 bug
android·前端·flutter
清岚_lxn6 小时前
原生SSE实现AI智能问答+Vue3前端打字机流效果
前端·javascript·人工智能·vue·ai问答
ZoeLandia6 小时前
Element UI 设置 el-table-column 宽度 width 为百分比无效
前端·ui·element-ui
橘子味的冰淇淋~7 小时前
解决 vite.config.ts 引入scss 预处理报错
前端·vue·scss
小小小小宇8 小时前
V8 引擎垃圾回收机制详解
前端
lauo9 小时前
智体知识库:ai-docs对分布式智体编程语言Poplang和javascript的语法的比较(知识库问答)
开发语言·前端·javascript·分布式·机器人·开源
拉不动的猪9 小时前
设计模式之------单例模式
前端·javascript·面试
一袋米扛几楼989 小时前
【React框架】什么是 Vite?如何使用vite自动生成react的目录?
前端·react.js·前端框架
Alt.99 小时前
SpringMVC基础二(RestFul、接收数据、视图跳转)
java·开发语言·前端·mvc