ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

史上最最最易懂的手写Promise原理及实现

2021-06-12 11:32:53  阅读:178  来源: 互联网

标签:resolve value reason 调用 Promise promise reject 易懂 手写


手写Promise原理及实现

文章目录


前言

在我们工作中经常遇到Promise,而且Promise面试的时候是必问题,可能会涉及一些运算,可能让封装个promise,可能更深入的问原理,那我们今天就从零开始封装一个promise


一、目的

复杂的异步流程,就会出现大量的回调函数嵌套,会导致回调地狱问题,为了避免这种问题,CommonJS社区提出Promise规范,为了给异步编程统一提供一种更合理更强大的解决方案,在ES2015被标准化,成为语言规范;

二、Promise的概述

1、promise是一个对象,用来表示异步任务最终结束之后成功还是失败,就像是内部对外部的一个承诺,一开始这个承诺是待定状态Pending,有可能成功Fulfilled也有可能失败Rejected,状态明确之后都会有相应的任务去执行,分别为onFulfilled和onRejected,而且Promise明确的特点,状态一旦确定就不会再发生改变
在这里插入图片描述
2、promise其实是一个全局类型,在new实例的时候,参数是一个函数,这个函数就可以看成兑现承诺的逻辑,这个函数会在构造primise的过程中被同步执行,两个函数参数resolve和reject,resolve()作用将primise对象状态修改为fulfilled,一般将异步任务的回调任务通过resolve(参数)参数传递出去,任务达成,反之失败,失败的时候我们会传递一个错误对象作为参数,来表述错误理由,如new Error(‘primise reject’);通过实例对象调用then(onFulfilled事件回调(参数),onRejected回调(参数));

三、Promise基本结构

我们先回顾一下,我们平时都是怎么使用Promise的:

const promise = new Promise(function (resolve, reject) {
  // 这里用于“兑现”承诺
  // resolve(100) // 承诺达成
  reject(new Error('promise rejected')) // 承诺失败
})

promise.then(function (value) {
  // 即便没有异步操作,then 方法中传入的回调仍然会被放入队列,等待下一轮执行
  console.log('resolved', value)
}, function (error) {
  console.log('rejected', error)
})

我们根据这个使用可以看出:
1)、Promise 就是一个类。在执行这个类的时候,需要传递一个执行器进去,执行器会立即执行。
2)、Promise 中有三种状态 分别为: 成功 fulfilled 、失败 rejected 、等待 pending
pending -> fulfilled
pending -> rejected
一旦状态确定就不可更改
3)、resolve和reject函数是用来更改状态的
resolve: fulfilled
reject: rejected
4)、then方法内部做的事情就判断状态 如果状态是成功 调用成功的回调函数 如果状态是失败 调用失败回调函数 then方法是被定义在原型对象中的(因为每个promise都可以调用到then方法)
思路:开始用class来定义一个类;constructor来接收执行器;执行器是立即执行的,并且有两个参数,resolve和reject,定义resolve、reject属性用箭头函数定义,// 为什么它们是箭头函数呢?因为我们在promise中调用的时候是直接调用的,那么在普通函数中this的指向是window或者undefined,定义成箭头函数目的就是为了这个函数内部的this指向这个类就是这个实例对象也就是promise对象;resolve和reject是用来更改状态的,所以要有状态这个属性,状态属性是promise独有的所以定义成实例属性,如: status = PENDING;//这个状态定义成常量而不是直接赋值字符串,是因为常量每次调用的时候代码有提示,而且能复用;默认值是等待;调用resolve函数的时候是改变状态的值 // 将状态更改为成功 this.status = FULFILLED;reject // 将状态更改为失败this.status = REJECTED;又因为状态一旦更改不可改变,所以如果状态不是等待 阻止程序向下执行 if (this.status !== PENDING) return;每个promise对象都有个then方法,它应该是定义在原型对象中,有两个参数,一个成功回调一个失败回调,在then中我们做的事是判断promise状态,如果this.status === FULFILLED,调用成功的回调,反之亦然;成功和失败回调都要一个参数,分别表示成功之后的值和失败的原因(默认值是undefined),这两个值我们会在resolve和reject中拿到,将他们保存下来,在then的两个参数中会用到

const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败

class MyPromise{
    constructor (executor) {
        executor(this.resolve, this.reject)
    }
    status = PENDING;
      // 成功之后的值(默认值是undefined)
    value = undefined;
    // 失败后的原因
    reason = undefined;
    resolve = value => {
        // 如果状态不是等待 阻止程序向下执行(因为一旦状态确定就不可更改)
        if (this.status !== PENDING) return;
        // 将状态更改为成功
        this.status = FULFILLED;
        // 保存成功之后的值 为什么要保存?因为要在then中拿到
        this.value = value;
      }
    reject = reason => {
        // 如果状态不是等待 阻止程序向下执行
        if (this.status !== PENDING) return;
        // 将状态更改为失败
        this.status = REJECTED;
        // 保存失败后的原因
         this.reason = reason;
         }
    then (successCallback, failCallback) {
        if (this.status === FULFILLED) {
            successCallback(this.value);
        }else if (this.status === REJECTED) {
            failCallback(this.reason);
        }
    }
}

测试封装的promise
index.ts

const MyPromise = require('./myPromise');

let promise = new MyPromise((resolve, reject) =>{
      resolve('成功')
  })
  promise.then((value)=>{
      console.log(value)
  },(reason)=>{
    console.log(reason)
  }) // 输出:成功

四、Promise异步执行

上面只是同步执行的情况,下面我们加上考虑异步执行情况
异步情况时如果用我们上述代码会出现一个情况:
执行顺序:执行器立即执行,里面有个定时器异步代码,主线程是不会等待异步代码执行的,所以将它们放在等待队列里,接着执行then方法,因为没有调用resolve或者reject所以状态肯定是等待的,我们封装的then方法中没有考虑状态是等待的情况,所以我们要判断一下状态,什么时候调它,当状态变为成功或失败的时候调相应的回调,所以先将成功和失败回调存储起来,2秒之后调用resolve或者reject时,先判断一下是否有成功或者失败回调,有就执行。

const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败

class MyPromise{
    constructor (executor) {
        executor(this.resolve, this.reject)
    }
    status = PENDING;
      // 成功之后的值(默认值是undefined)
    value = undefined;
    // 失败后的原因
    reason = undefined;
      // 成功回调
    successCallback =undefined;
    // 失败回调
    failCallback = undefined;
    resolve = value => {
        // 如果状态不是等待 阻止程序向下执行(因为一旦状态确定就不可更改)
        if (this.status !== PENDING) return;
        // 将状态更改为成功
        this.status = FULFILLED;
        // 保存成功之后的值 为什么要保存?因为要在then中拿到
        this.value = value;
         // 判断成功回调是否存在 如果存在 调用
         this.successCallback && this.successCallback(this.value);
      }
    reject = reason => {
        // 如果状态不是等待 阻止程序向下执行
        if (this.status !== PENDING) return;
        // 将状态更改为失败
        this.status = REJECTED;
        // 保存失败后的原因
         this.reason = reason;
         // 判断失败回调是否存在 如果存在 调用
         this.failCallback && this.failCallback(this.reason);
        }
    then (successCallback, failCallback) {
        if (this.status === FULFILLED) {
            successCallback(this.value);
        }else if (this.status === REJECTED) {
            failCallback(this.reason);
        }else {
            // 等待
            // 将成功回调和失败回调存储起来
            this.successCallback = successCallback;
            this.failCallback = failCallback;
    }
}

测试代码:

const MyPromise = require('./myPromise');

let promise = new MyPromise((resolve, reject) =>{
    setTimeout(function () {
        resolve('成功')
      }, 2000) 
  })
  promise.then((value)=>{
      console.log(value) // 延迟2s输出成功
  },(reason)=>{
    console.log(reason)
  }) 

五、promise的then 方法特性

同一个promise的then方法被多次调用

同一个promise下的then方法都是可以被多次调用的,如果我们多次调用同一个promise的then方法时,上述代码就不符合要求了
思路:当多次调用then的时候,也要分为同步和异步的情况,同步的情况是依次执行的(已经知道其状态了),不需要更改,异步情况时我们的then是要将所有传递函数都要执行,所以我们先要将其在then中一次存储起来,那么以前的存放函数的变量就要变成数组,在resolve和reject中一次执行这个参数

class MyPromise{
    constructor (executor) {
        executor(this.resolve, this.reject)
    }
    status = PENDING;
      // 成功之后的值(默认值是undefined)
    value = undefined;
    // 失败后的原因
    reason = undefined;
      // 成功回调
    successCallback =[];
    // 失败回调
    failCallback = [];
    resolve = value => {
        // 如果状态不是等待 阻止程序向下执行(因为一旦状态确定就不可更改)
        if (this.status !== PENDING) return;
        // 将状态更改为成功
        this.status = FULFILLED;
        // 保存成功之后的值 为什么要保存?因为要在then中拿到
        this.value = value;
         // 判断成功回调是否存在 如果存在 调用
       while(this.successCallback.length) this.successCallback.shift()(this.value);
      }
    reject = reason => {
        // 如果状态不是等待 阻止程序向下执行
        if (this.status !== PENDING) return;
        // 将状态更改为失败
        this.status = REJECTED;
        // 保存失败后的原因
         this.reason = reason;
         // 判断失败回调是否存在 如果存在 调用
         while(this.failCallback.length) this.failCallback.shift()(this.reason);
        }
    then (successCallback, failCallback) {
        if (this.status === FULFILLED) {
            successCallback(this.value);
        }else if (this.status === REJECTED) {
            failCallback(this.reason);
        }else {
            // 等待
            // 将成功回调和失败回调存储起来
            this.successCallback.push(successCallback);
            this.failCallback.push(failCallback);
    }
}
}

测试代码

```javascript

let promise = new MyPromise((resolve, reject) =>{
    setTimeout(function () {
        resolve('成功')
      }, 2000) 
  })
  promise.then((value)=>{
      console.log(value)
  })
  promise.then((value)=>{
      console.log(value+1)
  })
  promise.then((value)=>{
      console.log(value+2)
  })

promise的then方法被链式调用

promise的then方法是可以被链式调用的,下一个then方法回调函数的参数值实际上是上一个then方法回调函数的返回值。显然上面的代码不适用了
1)、思路:首先分为两个步骤去做:第一,我们要实现then方法的链式调用(首先我们要知道then方法是我们promise下面的方法,要想实现then方法的链式调用,那么每个then方法都应该返回一个promise对象。);第二如何将上一个then方法的返回值传递给下一个回调函数(首先找到下个then所对应的那个promise对象即promise2,在调用promise2中的resolve方法的时候将上一个返回值传递个它)。

class MyPromise{
    constructor (executor) {
        executor(this.resolve, this.reject)
    }
    status = PENDING;
      // 成功之后的值(默认值是undefined)
    value = undefined;
    // 失败后的原因
    reason = undefined;
      // 成功回调
    successCallback =[];
    // 失败回调
    failCallback = [];
    resolve = value => {
        // 如果状态不是等待 阻止程序向下执行(因为一旦状态确定就不可更改)
        if (this.status !== PENDING) return;
        // 将状态更改为成功
        this.status = FULFILLED;
        // 保存成功之后的值 为什么要保存?因为要在then中拿到
        this.value = value;
         // 判断成功回调是否存在 如果存在 调用
       while(this.successCallback.length) this.successCallback.shift()(this.value);
      }
    reject = reason => {
        // 如果状态不是等待 阻止程序向下执行
        if (this.status !== PENDING) return;
        // 将状态更改为失败
        this.status = REJECTED;
        // 保存失败后的原因
         this.reason = reason;
         // 判断失败回调是否存在 如果存在 调用
         while(this.failCallback.length) this.failCallback.shift()(this.reason);
        }
    then (successCallback, failCallback) {
    //既然要返回一个promise对象,那么我们先创建一个promise对象;原先代码去到执行器里面,都是立即执行的
    	let promise2 = new MyPromise((resolve,reject)=>{
	    	 if (this.status === FULFILLED) {
	          let x =  successCallback(this.value);
	          resolve(x);
	        }else if (this.status === REJECTED) {
	            failCallback(this.reason);
	        }else {
	            // 等待
	            // 将成功回调和失败回调存储起来
	            this.successCallback.push(successCallback);
	            this.failCallback.push(failCallback);
	    	}
    	}); 
    	return promise2;
	}
}

测试代码:

let promise = new MyPromise((resolve, reject) =>{
    setTimeout(function () {
        resolve('成功')
      }, 2000) 
  })
  promise.then((value)=>{
      console.log(value)
      return 100;
  }).then((value)=>{
      console.log(value)
  })

2)、思路:在then方法的回调函数中return的可以是个普通值也可以是个promise对象。如果是普通值我们可以直接调用resolve方法将这个普通值直接传给下个promise对象;如果返回的是个promise对象的话,我们要去查看这个promise状态,如果是成功的话,要调用resolve方法将这个成功的状态传递给下个promise对象,如果是失败的,我们调用reject方法将这个失败的的状态传递给下个promise对象;所以我们要做下面四件事:
1、判断 x 的值是普通值还是promise对象
2、如果是普通值 直接调用resolve
3、如果是promise对象 查看promsie对象返回的结果
4、再根据promise对象返回的结果 决定调用resolve 还是调用reject
因为我们在成功、失败、等待的时候都要去执行这个事情,所以提取出来resolvePromise(promsie2, x, resolve, reject)

const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败

class MyPromise {
  // 通过constructor来接收promise的参数也就是执行器
  constructor (executor) {
      // 下面定义resolve、reject属性,为什么它们是箭头函数呢?因为我们在promise中调用的时候是直接调用的,那么在普通函数中this的
      // 指向是window或者undefined,定义成箭头函数目的就是为了这个函数内部的this指向这个类就是这个实例对象也就是promise对象
      executor(this.resolve, this.reject)
  }
  // promsie 状态 
  status = PENDING; // 这个状态定义成常量而不是直接赋值字符串,是因为常量每次调用的时候代码有提示,而且能复用
  // 成功之后的值(默认值是undefined)
  value = undefined;
  // 失败后的原因
  reason = undefined;
  // 成功回调
  successCallback = [];
  // 失败回调
  failCallback = [];


  resolve = value => {
    // 如果状态不是等待 阻止程序向下执行(因为一旦状态确定就不可更改)
    if (this.status !== PENDING) return;
    // 将状态更改为成功
    this.status = FULFILLED;
    // 保存成功之后的值 为什么要保存?因为要在then中拿到
    this.value = value;
    // 判断成功回调是否存在 如果存在 调用
    // this.successCallback && this.successCallback(this.value);
    while(this.successCallback.length) this.successCallback.shift()(this.value)
  }
  reject = reason => {
    // 如果状态不是等待 阻止程序向下执行
    if (this.status !== PENDING) return;
    // 将状态更改为失败
    this.status = REJECTED;
    // 保存失败后的原因
    this.reason = reason;
    // 判断失败回调是否存在 如果存在 调用
    // this.failCallback && this.failCallback(this.reason);
    while(this.failCallback.length) this.failCallback.shift()(this.reason)
  }
  then (successCallback, failCallback) {
    let promsie2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
            let x = successCallback(this.value);
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve 
            // 如果是promise对象 查看promsie对象返回的结果 
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            resolvePromise( x, resolve, reject)
      }else if (this.status === REJECTED) {
            let x = failCallback(this.reason);
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve 
            // 如果是promise对象 查看promsie对象返回的结果 
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            resolvePromise( x, resolve, reject)
      } else {
        // 等待
        // 将成功回调和失败回调存储起来
        this.successCallback.push(successCallback);
        this.failCallback.push(failCallback);
      }
    });
    return promsie2;
  }
 }
function resolvePromise (x, resolve, reject) {
  if (x instanceof MyPromise) {
    // promise 对象  调用then来查看是成功还是失败状态,成功的就用resolve传递下去
    // x.then(value => resolve(value), reason => reject(reason)); 下面是简化写法
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}

测试

function other () {
  return new MyPromise((resolve, reject) {
    resolve('other');  
  })
}
promise.then(value=>{
	console.log(value);
	return other();
}).then(value=>{
	console.log(value);
	})

3)、虽然在then方法的回调函数中是可以return一个promise对象的,但有一种方法是例外的,在then的回调函数中是不可以返回当前then它所返回的promise对象的,如果你返回了then方法返回的promise对象,那么这个时候就发生了promise的循环调用,这是程序所不被允许的,会报错。(要根据演示一下系统promise报错情况来封装)
思路:在then中promise2就是我们所返回的promise对象,x是成功回调返回的值,如果这两个值相等,说明返回了自己,就会报错,要将错误信息传递给reject。这个判断我们在resolvePromise进行,所以还要传一个形参,返回的错误是类型错误;这里还有个问题,就是我们传递的参数promise2是在new promise执行完才会存在,而在调用resolvePromise的时候传递是拿不到这个值的,所以我们要将拿值的这一块处理成异步代码,我们可以用setTimeout来变成异步,这里目的是变成异步,而不是延迟,所以时间设置为0.

class MyPromise {
  // 通过constructor来接收promise的参数也就是执行器
  constructor (executor) {
      // 下面定义resolve、reject属性,为什么它们是箭头函数呢?因为我们在promise中调用的时候是直接调用的,那么在普通函数中this的
      // 指向是window或者undefined,定义成箭头函数目的就是为了这个函数内部的this指向这个类就是这个实例对象也就是promise对象
      executor(this.resolve, this.reject)
  }
  // promsie 状态 
  status = PENDING; // 这个状态定义成常量而不是直接赋值字符串,是因为常量每次调用的时候代码有提示,而且能复用
  // 成功之后的值(默认值是undefined)
  value = undefined;
  // 失败后的原因
  reason = undefined;
  // 成功回调
  successCallback = [];
  // 失败回调
  failCallback = [];


  resolve = value => {
    // 如果状态不是等待 阻止程序向下执行(因为一旦状态确定就不可更改)
    if (this.status !== PENDING) return;
    // 将状态更改为成功
    this.status = FULFILLED;
    // 保存成功之后的值 为什么要保存?因为要在then中拿到
    this.value = value;
    // 判断成功回调是否存在 如果存在 调用
    // this.successCallback && this.successCallback(this.value);
    while(this.successCallback.length) this.successCallback.shift()(this.value)
  }
  reject = reason => {
    // 如果状态不是等待 阻止程序向下执行
    if (this.status !== PENDING) return;
    // 将状态更改为失败
    this.status = REJECTED;
    // 保存失败后的原因
    this.reason = reason;
    // 判断失败回调是否存在 如果存在 调用
    // this.failCallback && this.failCallback(this.reason);
    while(this.failCallback.length) this.failCallback.shift()(this.reason)
  }
  then (successCallback, failCallback) {
    let promsie2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
      setTimeout(() => {
            let x = successCallback(this.value);
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve 
            // 如果是promise对象 查看promsie对象返回的结果 
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            resolvePromise(promsie2, x, resolve, reject)
       }, 0)
      }else if (this.status === REJECTED) {
      setTimeout(() => {
            let x = failCallback(this.reason);
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve 
            // 如果是promise对象 查看promsie对象返回的结果 
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            resolvePromise(promsie2, x, resolve, reject)
          }, 0)
         } else {
        // 等待
        // 将成功回调和失败回调存储起来
        this.successCallback.push(successCallback);
        this.failCallback.push(failCallback);
      }
    });
    return promsie2;
  }
}
function resolvePromise (promsie2, x, resolve, reject) {
  if (promsie2 === x) {
  // 是个类型错误,所以通过new 关键字创建错误
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (x instanceof MyPromise) {
    // promise 对象
    // x.then(value => resolve(value), reason => reject(reason));
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}

测试

let promise = new MyPromise((resolve, reject) =>{ 
        resolve('成功')
  })
  let p1 = promise.then((value)=>{
      console.log(value)
      return p1;
  });
  p1.then((value)=>{
      console.log(value)
  },(reason)=>{
      console.log(reason.message)
  })

六、Promise的错误捕获

我们在自己的mypromise中是没有进行任何错误处理的,为了程序的健壮性,我们要去捕获错误。
思路:处理错误;第一:处理的错误是执行器,当执行器执行过程中发生错误的时候,我们让promise的状态变成失败的状态并将错误原因传递下去,我们要在then第二个参数的地方捕获这个错误;第二:我们要处理then方法中的回调函数,如果这个函数执行过程中报错了,我们也要捕获到这个错误,这个错误要在下一个then方法的错误回调中捕获到,如果在执行回调时发生错误,我们要将这个错误通过reject传递给下个去;第三:在异步的执行中也要加上捕获错误
注:如果我们的then方法的失败回调函数中是没有错误的,执行成功的,那么它的返回值会在下个then方法的成功回调中接收

class MyPromise {
    // 通过constructor来接收promise的参数也就是执行器
    constructor (executor) {
        try{
            // 下面定义resolve、reject属性,为什么它们是箭头函数呢?因为我们在promise中调用的时候是直接调用的,那么在普通函数中this的
            // 指向是window或者undefined,定义成箭头函数目的就是为了这个函数内部的this指向这个类就是这个实例对象也就是promise对象
            executor(this.resolve, this.reject)
        }catch(e){
            this.reject(e);
        }
    }
    // promsie 状态 
    status = PENDING; // 这个状态定义成常量而不是直接赋值字符串,是因为常量每次调用的时候代码有提示,而且能复用
    // 成功之后的值(默认值是undefined)
    value = undefined;
    // 失败后的原因
    reason = undefined;
    // 成功回调
    successCallback = [];
    // 失败回调
    failCallback = [];
  
    resolve = value => {
        // 如果状态不是等待 阻止程序向下执行(因为一旦状态确定就不可更改)
        if (this.status !== PENDING) return;
        // 将状态更改为成功
        this.status = FULFILLED;
        // 保存成功之后的值 为什么要保存?因为要在then中拿到
        this.value = value;
        // 判断成功回调是否存在 如果存在 调用
        // this.successCallback && this.successCallback(this.value);
        while(this.successCallback.length) this.successCallback.shift()()
      }
      reject = reason => {
        // 如果状态不是等待 阻止程序向下执行
        if (this.status !== PENDING) return;
        // 将状态更改为失败
        this.status = REJECTED;
        // 保存失败后的原因
        this.reason = reason;
        // 判断失败回调是否存在 如果存在 调用
        // this.failCallback && this.failCallback(this.reason);
        while(this.failCallback.length) this.failCallback.shift()()
      }
      then (successCallback, failCallback) {
        let promsie2 = new MyPromise((resolve, reject) => {
          // 判断状态
          if (this.status === FULFILLED) {
            setTimeout(() => {
              try {
                let x = successCallback(this.value);
                // 判断 x 的值是普通值还是promise对象
                // 如果是普通值 直接调用resolve 
                // 如果是promise对象 查看promsie对象返回的结果 
                // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                resolvePromise(promsie2, x, resolve, reject)
              }catch (e) {
                reject(e);
              }
            }, 0)
          }else if (this.status === REJECTED) {
            setTimeout(() => {
              try {
                let x = failCallback(this.reason);
                // 判断 x 的值是普通值还是promise对象
                // 如果是普通值 直接调用resolve 
                // 如果是promise对象 查看promsie对象返回的结果 
                // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                resolvePromise(promsie2, x, resolve, reject)
              }catch (e) {
                reject(e);
              }
            }, 0)
          } else {
            // 等待
            // 将成功回调和失败回调存储起来
            this.successCallback.push(() => {
              setTimeout(() => {
                try {
                  let x = successCallback(this.value);
                  // 判断 x 的值是普通值还是promise对象
                  // 如果是普通值 直接调用resolve 
                  // 如果是promise对象 查看promsie对象返回的结果 
                  // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                  resolvePromise(promsie2, x, resolve, reject)
                }catch (e) {
                  reject(e);
                }
              }, 0)
            });
            this.failCallback.push(() => {
              setTimeout(() => {
                try {
                  let x = failCallback(this.reason);
                  // 判断 x 的值是普通值还是promise对象
                  // 如果是普通值 直接调用resolve 
                  // 如果是promise对象 查看promsie对象返回的结果 
                  // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                  resolvePromise(promsie2, x, resolve, reject)
                }catch (e) {
                  reject(e);
                }
              }, 0)
            });
          }
        });
        return promsie2;
      }
  }
 function resolvePromise (promsie2, x, resolve, reject) {
    if (promsie2 === x) {
    // 是个类型错误,所以通过new 关键字创建错误
      return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if (x instanceof MyPromise) {
      // promise 对象
      // x.then(value => resolve(value), reason => reject(reason));
      x.then(resolve, reject);
    } else {
      // 普通值
      resolve(x);
    }
  }

测试代码:

let promise = new MyPromise((resolve, reject) =>{ 
    setTimeout(()=>{
            resolve('成功')
    },2000)     
  })
  promise.then((value)=>{    
      console.log(value)
      throw new Error('then error')
  },reason=>{
      console.log(reason)
  }).then((value)=>{
      console.log(value)
  },(reason)=>{
      console.log(reason.message)
  })

结果:
在这里插入图片描述

七、then方法的参数变成可选参数

我们都知道then方法有两个参数,一个是成功回调一个是失败回调,then方法的这两个参数实际上是可选参数,也就是说在调用then方法的时候我们可以不传递任何参数
如下代码:

var promise = new Promise(function(resolve,reject){
	resolve(100)
})
promise
	.then()
	.then()
	.then(value => console.log(value))

思路:如果遇到上面的情况我们应该怎么执行呢?在这种状态下promise的状态会依次向后传递,一直到有回调函数的then方法,也就说第三个then是可以拿到promise状态的
思路:.then() <=>then(value=>value),也就是说我们要看then方法的内部有没有参数,如果没有参数就给他补一个参数,这样就可以向后传递了

class MyPromise {
  // 通过constructor来接收promise的参数也就是执行器
  constructor (executor) {
    try {
      // 下面定义resolve、reject属性,为什么它们是箭头函数呢?因为我们在promise中调用的时候是直接调用的,那么在普通函数中this的
      // 指向是window或者undefined,定义成箭头函数目的就是为了这个函数内部的this指向这个类就是这个实例对象也就是promise对象
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e);
    }
  }
  // promsie 状态 
  status = PENDING; // 这个状态定义成常量而不是直接赋值字符串,是因为常量每次调用的时候代码有提示,而且能复用
  // 成功之后的值(默认值是undefined)
  value = undefined;
  // 失败后的原因
  reason = undefined;
  // 成功回调
  successCallback = [];
  // 失败回调
  failCallback = [];


  resolve = value => {
    // 如果状态不是等待 阻止程序向下执行(因为一旦状态确定就不可更改)
    if (this.status !== PENDING) return;
    // 将状态更改为成功
    this.status = FULFILLED;
    // 保存成功之后的值 为什么要保存?因为要在then中拿到
    this.value = value;
    // 判断成功回调是否存在 如果存在 调用
    // this.successCallback && this.successCallback(this.value);
    while(this.successCallback.length) this.successCallback.shift()()
  }
  reject = reason => {
    // 如果状态不是等待 阻止程序向下执行
    if (this.status !== PENDING) return;
    // 将状态更改为失败
    this.status = REJECTED;
    // 保存失败后的原因
    this.reason = reason;
    // 判断失败回调是否存在 如果存在 调用
    // this.failCallback && this.failCallback(this.reason);
    while(this.failCallback.length) this.failCallback.shift()()
  }
  then (successCallback, failCallback) {
    // 参数可选
    successCallback = successCallback ? successCallback : value => value;
    // 参数可选
    failCallback = failCallback ? failCallback: reason => { throw reason };
    let promsie2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = successCallback(this.value);
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve 
            // 如果是promise对象 查看promsie对象返回的结果 
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            resolvePromise(promsie2, x, resolve, reject)
          }catch (e) {
            reject(e);
          }
        }, 0)
      }else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = failCallback(this.reason);
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve 
            // 如果是promise对象 查看promsie对象返回的结果 
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            resolvePromise(promsie2, x, resolve, reject)
          }catch (e) {
            reject(e);
          }
        }, 0)
      } else {
        // 等待
        // 将成功回调和失败回调存储起来
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value);
              // 判断 x 的值是普通值还是promise对象
              // 如果是普通值 直接调用resolve 
              // 如果是promise对象 查看promsie对象返回的结果 
              // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
              resolvePromise(promsie2, x, resolve, reject)
            }catch (e) {
              reject(e);
            }
          }, 0)
        });
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason);
              // 判断 x 的值是普通值还是promise对象
              // 如果是普通值 直接调用resolve 
              // 如果是promise对象 查看promsie对象返回的结果 
              // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
              resolvePromise(promsie2, x, resolve, reject)
            }catch (e) {
              reject(e);
            }
          }, 0)
        });
      }
    });
    return promsie2;
  }
}
function resolvePromise (promsie2, x, resolve, reject) {
  if (promsie2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (x instanceof MyPromise) {
    // promise 对象
    // x.then(value => resolve(value), reason => reject(reason));
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}

测试代码:


let promise = new MyPromise((resolve, reject) =>{ 
    resolve('成功')   
  })
  promise
    .then()
    .then()
    .then((value)=>{
      console.log(value)
  }) // 成功

八、promise.all方法实现

promise. all方法解决异步并发问题,允许我们以异步代码调用的顺序得到异步代码执行的结果,a
ll方法中先传入谁,谁先调用。以数组作为参数,数组中可以填入任何值,包括普通值和promise对象,数组中值的顺序一定是我们得到结果的顺序。这个方法返回值也是个promise对象。有个特点,在all方法中所有的promise对象如果状态都是成功的,那么all方法最后的结果也是成功的,如果有一个失败最后结果也是失败的
思路:怎么调用的?一个类调用all方法,所以all方法一定是个静态方法,第一:返回一个promise对象,第二:循环数组,看是普通值还是promise对象,如果是普通值就放在结果数组中,如果是promise对象那么我们先执行promise对象,然后把结果放在结果数组中,是否是MyPromise类下的一个实例;封的这个all方法有个问题,for循环是一瞬间就执行完的,但是在这过程中参与了异步操作,我们要等待所以异步完成之后,才能调用resolve这个方法,为了解决这个问题,我们定义了一个index来记录执行了多少次addDate,与数组长度相等,才执行resolve这个方法

  static all (array) {
    let result = [];
    let index = 0;
    return new MyPromise((resolve, reject) => {
      function addData (key, value) {
        result[key] = value;
        index++;
        if (index === array.length) {
          resolve(result);
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        if (current instanceof MyPromise) {
          // promise 对象
          current.then(value => addData(i, value), reason => reject(reason))
        }else {
          // 普通值
          addData(i, array[i]);
        }
      }
    })
  }

九、Promise.resolve 方法的实现

特性:
1)、promise.resolve方法:将给定的值转化成promise对象,也就是说它的返回值就是一个promise对象
2)、如果参数是个固定的值,会将这个值作为参数传给then方法,如果参数是个promise对象,会将这个它原封不动的返回出去,供后面的then调用
原理:
判断一下这个参数是否是个promise对象,如果是原封不动的返回,如果不是就创建一个promise对象,将给定的值包裹在promise中,再将这个值返回就可以了

 static resolve (value) {
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value));
  }

十、Promise.finally 方法的实现

特性:
1)、promise.finally方法有两个特点,无论promise对象最后的状态是成功的还是失败的,finally中的回调函数始终都会执行一次,另一个就是在finally方法的后面可以调用then方法来拿到promise最终返回的结果
2)、这个方法不是静态方法,是定义在类的原型对象身上
思路:在finally的内部我们怎么知道这个promise的状态呢,then是知道的,所以调用then方法;系统提供的promise中是等待finally中return的promise执行完之后在执行后面的then方法,这时候需要借助resolve这个方法,看finally的参数执行完之后返回的是什么,如果是普通值,转换成promise对象,等待执行完毕,如果是promise对象,还是等待完成,返回这个value;

 finally (callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value);
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason })
    })
  }

十一、Promise.catch 方法的实现

特性:
1)、catch 方法是用来处理当前promise对象失败的情况的,也就是说then方法是可以不传递失败回调的,如果不传递失败回调,这个失败回调就会被catch方法所捕获。
2)、catch也是定义在原型对象上的
3)、因为这个catch方法后面也是可以继续链式调用的,所以要return出去
4)、实际上内部调用的then方法,只不过没有注册成功回调,只注册了失败回调
原理:只需在catch方法内部调用then方法就可以了,在调用then方法的时候在成功的那个地方传入undefined,在失败的那个地方传递回调函数,

  catch (failCallback) {
    return this.then(undefined, failCallback)
  }

十二、小结

手写完整Promise代码

const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败

class MyPromise {
  // 通过constructor来接收promise的参数也就是执行器
  constructor (executor) {
    try {
      // 下面定义resolve、reject属性,为什么它们是箭头函数呢?因为我们在promise中调用的时候是直接调用的,那么在普通函数中this的
      // 指向是window或者undefined,定义成箭头函数目的就是为了这个函数内部的this指向这个类就是这个实例对象也就是promise对象
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e);
    }
  }
  // promsie 状态 
  status = PENDING; // 这个状态定义成常量而不是直接赋值字符串,是因为常量每次调用的时候代码有提示,而且能复用
  // 成功之后的值(默认值是undefined)
  value = undefined;
  // 失败后的原因
  reason = undefined;
  // 成功回调
  successCallback = [];
  // 失败回调
  failCallback = [];


  resolve = value => {
    // 如果状态不是等待 阻止程序向下执行(因为一旦状态确定就不可更改)
    if (this.status !== PENDING) return;
    // 将状态更改为成功
    this.status = FULFILLED;
    // 保存成功之后的值 为什么要保存?因为要在then中拿到
    this.value = value;
    // 判断成功回调是否存在 如果存在 调用
    // this.successCallback && this.successCallback(this.value);
    while(this.successCallback.length) this.successCallback.shift()()
  }
  reject = reason => {
    // 如果状态不是等待 阻止程序向下执行
    if (this.status !== PENDING) return;
    // 将状态更改为失败
    this.status = REJECTED;
    // 保存失败后的原因
    this.reason = reason;
    // 判断失败回调是否存在 如果存在 调用
    // this.failCallback && this.failCallback(this.reason);
    while(this.failCallback.length) this.failCallback.shift()()
  }
  then (successCallback, failCallback) {
    // 参数可选
    successCallback = successCallback ? successCallback : value => value;
    // 参数可选
    failCallback = failCallback ? failCallback: reason => { throw reason };
    let promsie2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = successCallback(this.value);
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve 
            // 如果是promise对象 查看promsie对象返回的结果 
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            resolvePromise(promsie2, x, resolve, reject)
          }catch (e) {
            reject(e);
          }
        }, 0)
      }else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = failCallback(this.reason);
            // 判断 x 的值是普通值还是promise对象
            // 如果是普通值 直接调用resolve 
            // 如果是promise对象 查看promsie对象返回的结果 
            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
            resolvePromise(promsie2, x, resolve, reject)
          }catch (e) {
            reject(e);
          }
        }, 0)
      } else {
        // 等待
        // 将成功回调和失败回调存储起来
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value);
              // 判断 x 的值是普通值还是promise对象
              // 如果是普通值 直接调用resolve 
              // 如果是promise对象 查看promsie对象返回的结果 
              // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
              resolvePromise(promsie2, x, resolve, reject)
            }catch (e) {
              reject(e);
            }
          }, 0)
        });
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason);
              // 判断 x 的值是普通值还是promise对象
              // 如果是普通值 直接调用resolve 
              // 如果是promise对象 查看promsie对象返回的结果 
              // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
              resolvePromise(promsie2, x, resolve, reject)
            }catch (e) {
              reject(e);
            }
          }, 0)
        });
      }
    });
    return promsie2;
  }
  finally (callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value);
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason })
    })
  }
  catch (failCallback) {
    return this.then(undefined, failCallback)
  }
  static all (array) {
    let result = [];
    let index = 0;
    return new MyPromise((resolve, reject) => {
      function addData (key, value) {
        result[key] = value;
        index++;
        if (index === array.length) {
          resolve(result);
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        if (current instanceof MyPromise) {
          // promise 对象
          current.then(value => addData(i, value), reason => reject(reason))
        }else {
          // 普通值
          addData(i, array[i]);
        }
      }
    })
  }
  static resolve (value) {
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value));
  }
}

function resolvePromise (promsie2, x, resolve, reject) {
  if (promsie2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (x instanceof MyPromise) {
    // promise 对象
    // x.then(value => resolve(value), reason => reject(reason));
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}
  1. Promise 就是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行
  2. Promise 中有三种状态 分别为 成功 fulfilled 失败 rejected 等待 pending
    pending -> fulfilled
    pending -> rejected
    一旦状态确定就不可更改
  3. resolve和reject函数是用来更改状态的
    resolve: fulfilled
    reject: rejected
  4. then方法内部做的事情就判断状态 如果状态是成功 调用成功的回调函数 如果状态是失败 调用失败回调函数 then方法是被定义在原型对象中的(因为每个promise都可以调用到then方法)
  5. then成功回调有一个参数 表示成功之后的值 then失败回调有一个参数 表示失败后的原因
  6. 同一个promise对象下面的then方法是可以被调用多次的
  7. then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值
  8. then方法返回的promise对象和调用的对象,不能是同一个
  9. promise的错误捕获:执行器、回调函数、异步中。如果then方法的失败回调函数中是没有错误的,执行成功的,那么它的返回值会在下个then方法的成功回调中接收
  10. then方法的参数是可选参数,promise的状态会依次向后传递,一直到有回调函数的then方法
  11. promise.all方法:解决异步并发问题
  12. promise.resolve方法:将给定的值转化成promise对象
  13. promise.finally方法有两个特点,无论promise对象最后的状态是成功的还是失败的,finally中的回调函数始终都会执行一次,另一个就是在finally方法的后面可以调用then方法来拿到promise最终返回的结果
  14. catch 方法是用来处理当前promise对象失败的情况的,也就是说then方法是可以不传递失败回调的,如果不传递失败回调,这个失败回调就会被catch方法所捕获

标签:resolve,value,reason,调用,Promise,promise,reject,易懂,手写
来源: https://blog.csdn.net/smilelixiaojie/article/details/117594854

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有