Promise

79 阅读4分钟

介绍

  • 异步调用必须要通过回调函数来返回数据,当我们进行一些复杂调用时, 会出现“回调地狱”
  • promise出现的原因是为了解决异步函数调用的问题, 防止代码可读写性太差
  • promise就是一个用来存储数据的容器,它和普通对象的区别就是它可以存储异步调用的结果

方法详情

  • Promise构造函数的回调函数,它会在创建Promise时调用,调用时会有两个参数传递进去,resolve和reject, resolve在执行正常时存储数据, reject在执行错误时存储数据

  • 读取数据采用then,then需要两个回调函数作为参数,回调函数用来获取promise中的数据,通过resolve存储的数据会调用第一个函数返回, 可以在第一个函数中编写处理数据的代码,通过reject存储的数据或出现异常时, 会调用第二个函数返回, 可以在第二个函数中编写处理异常的代码

  • Promise中维护了两个隐藏属性

      PromiseResult
          - 用来存储数据
      PromiseState
          - 记录Promise的状态(三种状态)
              pending  (进行中)
              fulfilled(完成) 通过resolve存储数据时
              rejected(拒绝,出错了) 出错了或通过reject存储数据时
          - state只能修改一次,修改以后永远不会在变
      流程:
          当Promise创建时,PromiseState初始值为pending,
              当通过resolve存储数据时 PromiseState 变为fulfilled(完成)
                  PromiseResult变为存储的数据
              当通过reject存储数据或出错时 PromiseState 变为rejected(拒绝,出错了)
                  PromiseResult变为存储的数据 或 异常对象
    
          当我们通过then读取数据时,相当于为Promise设置了回调函数,
              如果PromiseState变为fulfilled,则调用then的第一个回调函数来返回数据
              如果PromiseState变为rejected,则调用then的第二个回调函数来返回数据
    
  • then 和 catch 和 finally

      then()和catch()都会返回一个新的promise, promise会存储回调函数的返回值
      finally的返回值, 不会存储到新的promise中
      catch() 用法和then类似,但是只需要一个回调函数作为参数
          catch()中的回调函数只会在Promise被拒绝时才调用
          catch() 相当于 then(null, reason => {})
          catch() 就是一个专门处理Promise异常的方法
    
      finally() 
          - 无论是正常存储数据还是出现异常了,finally总会执行
          - 但是finally的回调函数中不会接收到数据
          - finally()通常用来编写一些无论成功与否都要执行代码
          
    

方法注意

  • promise的链式调用
    const promise = new Promise((resolve,reject) => {
        resolve('哈哈果1')
    })
    promise.then(result => console.log('第一个then'))
    .catch(err => console.log('第一个catch'))
    .then(result => console.log(result,'result'))

对于Promise进行链式调用时,后边的方法(then和catch)读取的是上一步的执行结果,如果上一步的执行结果不是当前想要的,则跳过当前方法

Promise.all

    Promoise.all([...]) 同时返回多个Promise的执行结果,其中一个有报错就返回错误

Promise.allSettled()

    Promise.allSettled([...])同时返回多个Promise的执行结果(无论失败与否)
    返回 {status: 'fulfilled', value: '123'} {status: 'rejected', reason: '123'}

Promise.race()

    Promise.race([...])返回执行最快的, 不考虑对错
    

实现一个Promise

/**
 * Promise方法的核心在于解决异步,承诺存值
 * 只存一次, 不会丢, 同时then实现promise的链式调用
 * then返回的是什么: 返回的是promise中存的值
 * queueMicrotask(() => {}) 创建微任务队列
 * 私有方法和私有属性外边实例不可访问加#表示私有
 *
 * 类中方法: 如果是普通函数形式是放在了函数的原型中 [只有一次]
 *          如果是箭头函数形式是放在了对象中 [这种情况稍微浪费一点内存]
 *  - MyPromiseStatus记录Promise的状态(三种状态)
 *          pending  (进行中)
 *          fulfilled(完成) 通过resolve存储数据时
 *          rejected(拒绝,出错了) 出错了或通过reject存储数据时
 * 状态一旦改变就不会再被修改,所以使用状态来判断是否赋值
 *
 */
const MyPromiseStatus = {
  PENDING_STATUS: 0,
  FULFILLED_STATUS: 1,
  REJECTED_STATUS: 2,
};

class MyPromise {
  #result = null; //用来存储值
  #status = MyPromiseStatus.PENDING_STATUS;
  #onFulfilledCallbacks = []; //用来执行成功的回调函数 []为了实现then的多次调用
  #onRejectedCallbacks = []; //用来执行回调函数

  constructor(executor) {
    executor(this.#resolve, this.#reject);
  }
  // resolve 和 reject 是内部方法, 不允许外边修改
  #resolve = (value) => {
    if (this.#status !== MyPromiseStatus.PENDING_STATUS) return;
    this.#result = value;
    this.#status = MyPromiseStatus.FULFILLED_STATUS;
    queueMicrotask(() => {
      this.#onFulfilledCallbacks.forEach((cb) => {
        cb(this.#result);
      });
    });
  };

  #reject = (value) => {
    if (this.#status !== MyPromiseStatus.PENDING_STATUS) return;
    this.#result = value;
    this.#status = MyPromiseStatus.REJECTED_STATUS;
    queueMicrotask(() => {
      this.#onRejectedCallbacks.forEach((cb) => {
        cb();
      });
    });
  };
  // 存值完成, 定义取值方法
  then(onFulfilled, onRejected) {
    /**
     * 谁将成为then返回的新Promise中的数据
     *  then中回调函数的返回值,会成为新的Promise中的返回值
     */
    // 函数可以不传
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (reason) => {
            return reason;
          };
    return new MyPromise((resolve, reject) => {
      if (this.#status === MyPromiseStatus.FULFILLED_STATUS) {
        //成功
        queueMicrotask(() => {
          resolve(onFulfilled(this.#result));
        });
      } else if (this.#status === MyPromiseStatus.REJECTED_STATUS) {
        //失败
        queueMicrotask(() => {
          reject(onRejected(this.#result));
        });
      } else {
        // 进行中 --- 如果是异步的时候就要在resolve和reject调用方法
        this.#onFulfilledCallbacks.push(() => {
          resolve(onFulfilled(this.#result));
        });
        this.#onRejectedCallbacks.push(() => {
          reject(onRejected(this.#result));
        });
      }
    });
  }
  catch(onRejected) {
    return this.then(null, onRejected);
  }
}

export default MyPromise;