Promise及其API源码的实现思考过程

1、Promise&&then

我们知道,promise是为了解决异步回调地狱而出现的新的解决方案,它是主要特点是可以通过then拿到异步的结果,then又能返回新的异步结果,形成链式调用

javascript 复制代码
new Promise((resolve,reject)=>{
resolve('结果')
}).then(res=>{
return res
}).then(...)

我们也知道,在promise中有三个状态,pending进行中,fulfilled已完成,rejected已失败,且一旦pending改变,不会再被更改。

所以先根据以上信息,实现:可以通过then链式调用

ini 复制代码
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class myPromise {
    constructor(executor) {
        this.status = PENDING;
        this.value = undefined;
        this.reason = undefined;
        // this.onFulfilledCbs = [];
        // this.onRejectedCbs = [];

        const resolve = (value) => {
            if (this.status === PENDING) {
                this.status = FULFILLED;
                this.value = value
                // this.onFulfilledCbs.forEach(cb => cb(value))
            }
        }
        const reject = (reason) => {
            if (this.status === PENDING) {
                this.status = REJECTED;
                this.reason = reason
                // this.onRejectedCbs.forEach(cb => cb(reason))
            }
        }

        executor(resolve, reject)
    }
    then(onFulfilled, onRejected) {
        return new myPromise((resolve, reject) => {
            let result = undefined;
            if (this.status === FULFILLED) {
                result = onFulfilled(this.value)
                resolve(result)
            }
            if (this.status === REJECTED) {
                result = onRejected(this.reason)
                reject(result)
            }
            // if (this.status === PENDING) {
            //     this.onFulfilledCbs.push(onFulfilled)
            //     this.onRejectedCbs.push(onRejected)
            // }
        })
    }
}
new myPromise((resolve, reject) => {
    // setTimeout(() => {
        resolve(111)
    // }, 1000)
}).then(res => {
    console.log('res', res)
})

根据以上代码,在then中再return promise,实现了简单的链式调用,且需要setTimeout模拟异步,在then中拿到异步操作返回的结果,因为是异步的,当执行then时status还是pending,需要把成功和失败回调存储,等到resolve或reject时拿出来执行。(这部分代码实现把以上注释部分取消即可。)

根据promise的约定,resolve接收的参数可以有4种,所以需要兼容一下,目前只有一种就是基础值,还有promise、thenable对象、空,且resolve出的值不能是promise自身,否则会造成无限循环引用。

同时用setTimeout将异步模拟一下,有如以下代码:

ini 复制代码
// promise有三种状态,分别是pending.FULFILLED ,rejected,pending是初始状态,FULFILLED 是成功状态,rejected是失败状态
// 状态一旦改变,不会再变
const PENDING = "pending";
const FULFILLED = "FULFILLED ";
const REJECTED = "rejected";
class Promise1 {
  constructor(excutor) {
    // 其实resolve,reject这两个函数不是他们传来的,只是他们调用的内部的方法,它们传来的只是值
    this.status = PENDING;
    this.value = undefined;
    this.error = undefined;
    this.onFulfilledCallbacks = []; // 存储then方法中onFULFILLED 的回调函数
    this.onRejectedCallbacks = []; // 存储then方法中onRejected的回调函数

    const resolve = (value) => {
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onFulfilledCallbacks.forEach((callback) => callback());
      }
    };
    const reject = (error) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.error = error;
        this.onRejectedCallbacks.forEach((callback) => callback());
      }
    };
    try {
      excutor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  then(onFulfilled, onRejected) {
    // 处理onFULFILLED 不是函数的情况
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    // 处理onRejected不是函数的情况
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (error) => {
            throw error;
          };
    // then最终还是会return一个promise
    const promise = new Promise((resolve, reject) => {
      const handleFulfilled = () => {
        try {
          const result = onFulfilled(this.value);
          resolvePromise(promise, result, resolve, reject);
        } catch (error) {
          reject(error);
        }
      };

      const handleRejected = () => {
        try {
          const result = onRejected(this.error);
          resolvePromise(promise, result, resolve, reject);
        } catch (error) {
          reject(error);
        }
      };

      if (this.status === FULFILLED) {
        // 为什么这里需要setTimeout,因为要模拟promise是异步的
        setTimeout(handleFulfilled, 0);
      } else if (this.status === REJECTED) {
        setTimeout(handleRejected, 0);
      } else {
        this.onFulfilledCallbacks.push(() => setTimeout(handleFulfilled, 0));
        this.onRejectedCallbacks.push(() => setTimeout(handleRejected, 0));
      }
    });
    return promise;
  }
}
function resolvePromise(promise, result, resolve, reject) {
  // 如果promise和result是同一个对象,则抛出TypeError,
  if (promise === result) {
    return reject(new TypeError("Chaining cycle detected for promise"));
  }
  // 如果result是thenable对象
  if (result && (typeof result === "object" || typeof result === "function")) {
    try {
      const then = result.then;
      if (typeof then === "function") {
        // then就是接收两个参数:resolve或reject,然后出结果的函数,直接执行它,但注意this指向,并传参
        then.call(
          this,
          (value) => {
            resolvePromise(promise, value, resolve, reject);
          },
          (reason) => reject(reason)
        );
      } else {
        resolve(result);
      }
    } catch (error) {
      reject(error);
    }
  } else {
    // 否则直接调用resolve
    resolve(result);
  }
}

2、catch

then后面除了可以跟着then,也可以通过catch方法捕获错误,它等同于then中的第二个参数,所以通过调用then,将catch中的回调函数传给then第二个参数即可。

kotlin 复制代码
// 声明在Promise类中
catch(cb){
  return this.then(_,cb)
}

3、finally

Promise的实例方法还有一个叫finally,是不管成功还是失败都会执行的,还是用then方法返回结果,不管成功还是失败结果都resolve返回一个promise,从而catch后面也可继续链式调用。then接收两个参数,成功后的回调,这个回调的参数是成功返回的值,所以finally若想

javascript 复制代码
// 声明在Promise类中
finally(cb){
    return this.then(
        value=>Promise1.resolve(cb()).then(()=>value)
        reason=>Promise1.resolve(cb()).then(()=>reason)
)}

4、resolve

在Promise类上,有resolve,reject,race,all,allSettled等类方法,接下来一一演示,Promise.resolve(1)就是等同于new Resolve(()=>{resolve(1)}),既然如此直接返回后者,再加个判断,如果不是promise才需要包装,是promise直接返回

scss 复制代码
static resolve(value){
 if(value instanceof Promise1){
 return value;
 }
 return new Promise1(()=>resolve(value))
}

5、reject

reject同理可得,Promise.reject('err')就是等同于new Promise1(()=>{reject('err')})

javascript 复制代码
static reject(reason){
return new Promise1((_,reject)=>reject(reason))
}

6、race

race、all、allSettled都是为了解决多个promise同时执行的,race是当其中一个先执行完毕,多个promise实例的执行结果就是这个最先完成的状态。 既然是多个,那参数就是数组,我们只需去遍历,最先执行resolve的,也就是最先改变状态的,后续即使继续遍历,也不再改变状态。

怎么知道它何时resolve呢,因为我们知道then中的回调是在成功时调用的,所以通过Promise.resolve包装,在then中去resolve实例

javascript 复制代码
static race(arr){
 return new Promise1((resolve,reject)=>{
  arr.forEach(pro=>{
   Promise1.resolve(pro).then((value)=>{
     resolve(pro)
   },reason=>{
     reject(reason)
   })
  })
 })
}

7、all

all是多个promise实例,只要有一个失败,结果就是失败,全部成功才算成功。所以要计算成功的次数,数组长度等于成功的个数后,再resolve结果数组。

ini 复制代码
  static all(arr) {
    return new Promise1((resolve, reject) => {
      let res = [];
      let completeCount = 0;
      if (arr.length === 0) {
        resolve(res);
        return;
      }
      arr.forEach((pro) => {
        Promise1.resolve(pro).then(
          (value) => {
            res[completeCount] = value;
            completeCount++;
            if (completeCount === arr.length) {
              resolve(res);
            }
          },
          (err) => {
            reject(err);
          }
        );
      });
    });
  }
}

8、allSettled

allSettled是不管成功还是失败,都会返回结果的。注意不管当前是成功还是失败,当执行到最后一个时,都是resolve出数组结果,且结果数组对象是由status和结果值组成的,如果成功status='fulfilled',失败status='rejected'

ini 复制代码
  static allSettled(promises) {
    return new CustomPromise((resolve, reject) => {
      const results = [];
      let completedCount = 0;
      if (promises.length === 0) {
        resolve(results);
        return;
      }
      promises.forEach((promise, index) => {
        CustomPromise.resolve(promise).then(
          (value) => {
            results[index] = {
              status: "fulfilled",
              value,
            };
            completedCount++;
            if (completedCount === promises.length) {
              resolve(results);
            }
          },
          (reason) => {
            results[index] = {
              status: "rejected",
              reason,
            };
            completedCount++;
            if (completedCount === promises.length) {
              resolve(results);
            }
          }
        );
      });
    });
  }

根据以上,留一个作业,any方法,只要有一个成功,包装实例返回结果fulfilled,或者全部都失败,结果才会是rejected

相关推荐
—Qeyser39 分钟前
用 Deepseek 写的uniapp血型遗传查询工具
前端·javascript·ai·chatgpt·uni-app·deepseek
codingandsleeping40 分钟前
HTTP1.0、1.1、2.0 的区别
前端·网络协议·http
小满blue42 分钟前
uniapp实现目录树效果,异步加载数据
前端·uni-app
喜樂的CC2 小时前
[react]Next.js之自适应布局和高清屏幕适配解决方案
javascript·react.js·postcss
天天扭码2 小时前
零基础 | 入门前端必备技巧——使用 DOM 操作插入 HTML 元素
前端·javascript·dom
咖啡虫3 小时前
css中的3d使用:深入理解 CSS Perspective 与 Transform-Style
前端·css·3d
烛阴3 小时前
手把手教你搭建 Express 日志系统,告别线上事故!
javascript·后端·express
拉不动的猪3 小时前
设计模式之------策略模式
前端·javascript·面试
旭久3 小时前
react+Tesseract.js实现前端拍照获取/选择文件等文字识别OCR
前端·javascript·react.js
独行soc3 小时前
2025年常见渗透测试面试题-红队面试宝典下(题目+回答)
linux·运维·服务器·前端·面试·职场和发展·csrf