实现简单的手写原生Promise

974 阅读6分钟

一、Promise 特点

  1. 对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:
  • pending: 初始状态,不是成功或失败状态。
  • resolve/fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。
  1. 一旦状态改变,就不会再变。
  • pendding=>resolve/fulfilled;
  • pendding=>rejected;

二、Promise 优缺点

  1. 优点:
  • 链式调用,解决回调地狱问题
  • 指定回调函数的方式更加灵活
  1. 缺点:
  • 无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
  • 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
  • 当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

三、主要异步任务

  • Ajax请求
  • setTimeout定时器
  • fs文件流操作等
  • ...

四、Promise 的简单使用

// fs 异步操作
const fs = require('fs');
const pro_obj = new Promise((resolve, rejects) => {
  fs.readFile('./demo.txt', (err, data) => {
    if (err) rejects(err);
    resolve(data);
  });
});
pro_obj.then(
  (value) => {
    console.log(value.toString());
  },
  (err) => {
    console.log(err);
  }
);

五、 Promise API

  1. Promise 构造函数: Promise(excutor){}
  • executor 函数:执行器 (resolve,reject)=>{}
  • resolve 函数:内部定义成功时调用的函数 value=>{}
  • reject 函数: 内部定义失败时调用的函数 reson=>{} 注:执行器函数会在Promise内部同步调用,异步操作再执行器中执行
  1. Promise.prototype.then 方法: (onResolved,onRejected)=>{}
  • onResolved 函数: 成功的回调函数 (value)=>{}
  • onRejected 函数:失败的回调函数 (reson)=>{} 注:成功或者失败的回调函数返回的是一个新的Promise对象
  1. Promise.prototype.catch方法: (onRejected)=>{}
  • onRejected 函数:失败的回调函数 (reson)=>{}
  1. Promise.resolve 方法:(value)=>{}
  • value:成功的数据或者Promise对象 (返回一个成功/失败的的Promise对象)
let p1 = Promise.resolve(521)

// 传入的参数为非Promise类型的对象,则返回的结果是成功的Promise对象

console.log(p1) //Promise { 521 }

// 传入的参数为Promise类型的对象,参数的结果决定了resolve的结果

let p2 = Promise.resolve(new Promise((resolve, reject) => {
  
  resolve('ok')
  
}))

console.log(p2) // Promise { 'ok' } 
  1. Promise.reject方法:(reson)=>{}
  • reson: 返回失败的原因
let p1 = Promise.reject(521)
console.log(p1) //Promise { <rejected> 521 }

let p2 = Promise.reject(new Promise((resolve, reject) => {
  resolve(521)
}))
console.log(p2) //Promise { <rejected> Promise { 521 } } 

// 即使是实例对象的回调函数是成功的,reject返回的还是失败的结果
  1. Promise.all 方法:(promises)=>{}
  • promises:包含n个promise的数组

注:返回一个新的promise,只有所有的promise都成功才算成功,只要一个失败了就直接失败

let p1 = new Promise((resolve, reject) => {
  resolve('ok');
});
let p2 = Promise.resolve('success');
let p3 = Promise.resolve('okkkk');
let p4 = Promise.reject('error');
const result = Promise.all([p1, p2, p3]); 
const err = Promise.all([p1, p2, p3, p4]); 

console.log(result); 
console.log(err); 

全部成功结果 result:

有一个失败结果 err:

  1. Promise.race 方法: (promises)=>{}
  • promises:包含n个promise的数组

注:返回一个新的promise对象,是第一个成功的的promise的结果状态

			let p1 = new Promise((resolve, reject) => {
        reject('ok');
      });
      let p2 = Promise.reject('success');
      let p3 = Promise.resolve('okkkk');
      const result = Promise.race([p1, p2, p3]);
      console.log(result);

六、Promise关键问题

1. 修改Promise状态的方法
  • 执行reject方法 pending=> reject
  • 执行resolve方法 pending =>resolve
  • 丢出错误throw pending=>reject

2.能否执行多个回调

当promise状态改变时,它所定义的多个成功/失败的回调函数都会执行

3.改变promise的状态和指定回调函数谁先谁后(resolve、reject先执行还是then、catch先执行)
  1. 都有可能,正常情况下是先指定回调再改变状态,但也可以先改变状态再指定回调(看执行器内部的任务队列是同步任务还是异步任务,) 如何先改变状态再指定回调
  2. 在执行器中直接调用resolve或者reject
  3. 延迟更长时间才调用then() 什么时候拿到数据
  4. 如果先指定的回调,那么当状态改变时,回调函数就会调用,就能得到数据
  5. 如果先改变的状态,那当指定回调是,回调函数就会调用,得到数据
4.Promise then 方法的返回的新的promise的结果状态由什么决定?
  1. 简单表达: 由then方法指定的回调函数执行的结果决定

  2. 详细表达:

    a. 如果抛出异常,新的Promise变为rejected,reson为抛出的异常

    b. 如果返回的是非promise的任意值,新promise变为resolved,value为返回的值

    c. 如果返回的是另外一个新的Promise ,此promise的结果就会成为新的promise的结果

5. Promise如何串联多个操作任务

可以写成then方法的链式调用(原理:promise的then方法返回的还是promise对象)

6. Promise 异常穿透

当使用promise的then的链式调用时,可以在最后指定失败的回调函数catch,当前面任何操作出了异常都会被传到追后失败的回调中处理

7. 中断promise链

当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数。方法:在回调函数中返回一个pendding状态的promise对象

七、手写源码

/**
 * 1. 搭建Promise基础架构
 *      a.Promise接受executor作为参数传入
 *      b.设置内置属性[PromiseStatus、PromiseResult]和方法[resolve、reject]
 *          Ⅰ. PromiseStatus有三种状态,分别为pending,fulfilled,rejected,状态只能改变一次,pending=>fuifilled pending=>rejected
 *          Ⅱ. PromiseResult 执行的结果保存在PromiseResult中
 *      c. 使用try catch来获取executor中抛出的错误,并返回Promise对象处理抛出的异常
 * 2. then方法
 *     a. then方法执行回调函数需要有条件,根据状态执行对应的回调函数,函数的实参是存储在PromiseResult的值
 *     b. 处理异步状态,实例对象内部为异步函数,状态是pending ,then方法的回调没有等到pending状态的改变,导致then方法不执行,解决方法:定义callbacks回调数组
 * 3. 实现catch方法
 * 4. 实现resolve、reject方法
 * 5. 实现all、race方法
 *
 * @param {*} executor
 */
function Promise(executor) {
  this.PromiseStatus = 'pending';
  this.PromiseResult = null;
  this.callbacks = [];

  resolve = (data) => {
    if (this.PromiseStatus !== 'pending') return;
    this.PromiseResult = data;
    this.PromiseStatus = 'fulfilled';
    // 复现原生Promise的异步调用机制
    setTimeout(() => {
      this.callbacks.forEach((item) => item.onResolved(data));
    });
  };
  reject = (data) => {
    if (this.PromiseStatus !== 'pending') return;

    this.PromiseResult = data;
    this.PromiseStatus = 'rejected';
    // 复现原生Promise的异步调用机制
    setTimeout(() => {
      this.callbacks.forEach((item) => item.onRejected(data));
    });
  };
  try {
    executor(resolve, reject);
  } catch (error) {
    reject(error);
  }
}

Promise.prototype.then = (onResolved, onRejected) => {
  // 异常穿透
  if (typeof onResolved !== 'function') {
    onResolved = (data) => data;
  }
  if (typeof onRejected !== 'function') {
    onRejected = (reason) => {
      throw reason;
    };
  }
  return new Promise((resolve, reject) => {
    const callback = (type) => {
      try {
        let result = type(this.PromiseResult);
        if (result instanceof Promise) {
          result.then(
            (r) => resolve(r),
            (j) => reject(j)
          );
        } else {
          resolve(result);
        }
      } catch (error) {
        reject(error);
      }
    };
    if (this.PromiseStatus === 'fulfilled') {
      callback(onResolved);
    }
    if (this.PromiseStatus === 'rejected') {
      callback(onRejected);
    }
    if (this.PromiseStatus === 'pending') {
      this.callbacks.push({
        onResolved: callback(onResolved),
        onRejected: callback(onRejected),
      });
    }
  });
};

Promise.prototype.catch = function (onRejected) {
  return this.then(undefined, onRejected);
};

Promise.resolve = function (value) {
  return new Promise((resolve, reject) => {
    if (value instanceof Promise) {
      value.then(
        (r) => {
          resolve(r);
        },
        (j) => {
          reject(j);
        }
      );
    } else {
      resolve(value);
    }
  });
};

Promise.reject = function (value) {
  return new Promise((resolve, reject) => {
    reject(value);
  });
};

Promise.all = function (pArray) {
  return new Promise((resolve, reject) => {
    let count = 0;
    let pResult = [];
    for (let i = 0; i < pArray.length; i++) {
      pArray[i].then(
        (v) => {
          count++;
          pResult[i] = v;
          if (count === pArray.length) {
            resolve(pResult);
          }
        },
        (j) => {
          reject(j);
        }
      );
    }
  });
};

Promise.race = function (pArray) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < pArray.length; i++) {
      pArray[i].then(
        (r) => {
          resolve(r);
        },
        (j) => {
          reject(j);
        }
      );
    }
  });
};