编程语言
首页 > 编程语言> > Promise手写源码以及api详解-----学了就会学完就忘系列

Promise手写源码以及api详解-----学了就会学完就忘系列

作者:互联网

一、 Promise 核心逻辑

  1. promise 就是一个类,在执行这个类的时候 需要传递一个执行器(也就是里面这个回调函数,箭头函数),执行器会立即执行( 当我们new Promise 的时候 回调会立即调用)
  2. 回调函数有两个参数resolve,reject,这两个参数其实就是两个函数
  3. Promise 中有三种状态,分别为
    成功 fulfilled
    失败 rejected
    等待 pending
  4. 状态特点:
    等待变成成功 pending ->fulfilled
    等待变成失败 pending ->rejected
    一旦状态确定成功/失败,就不可以改变
  5. resolve 和reject 函数是用来更改状态的
    resolve ->fulfilled
    reject ->rejected
  6. 我们创建完promise 对象以后,我们可以创建一个变量去接收
    promise 下面可以调用then方法来传递两个回调函数,成功与失败,也就说当我们调用then 在这里插入代码片方法的时候我们要去判断promise的返回状态,如果成功则调用的第一个(成功的回调函数),失败则第二个(失败的回调函数)
  7. then 方法内部做的事情就是判断状态,如果成功则调用的第一个(成功的回调函数),失败则第二个(失败的回调函数), then 成功回调(successCallback)有一个参数,表示成功之后返回的值valuethen失败回调(failCallback)后有一个返回值reason失败原因

二、 Promise 基础代码

  1. 通过class关键字创建一个类 MyPromise
  2. 我们需要使用构造函数constructor 来接受这个构造函数,executor代表执行器(执行器是立即执行的,执行器就代表回调函数)
/*
    1、通过`class`关键字创建一个类 MyPromise
    2、我们需要使用构造函数constructor 来接受这个构造函数,executor代表执行器(执行器是立即执行的,执行器就代表回调函数)
*/
const PENGDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        // 当前我们是在一个类里面,我们要通过this去访问
        executor(this.resolve, this.reject)

    }
    /*
       1、 为什么定义成箭头函数?
        将来在调用resolve 和reject的时候是直接调用的,如果是普通函数,函数里面的指向-window/reject,如果是箭头函数,会改变this指向,是内部指向类的实例对象,指向promise 对象
        2、 resolve 和reject 就是用来更改状态的。
        3、我们要求定义状态,定义成常量,好处:复用,便于开发。
      
    */
    // 初始状态PENGDING

    status = PENGDING
    /*
        8、设置默认成功之后的值,失败之后的原因
    */
    value = undefined
    reason = undefined
    /*
        9、成功回调,失败回调诉默认值
    */
    successCallback = undefined
    failCallback = undefined
    resolve = value => {
        /*
            4、如果状态不是等待,阻止程序向下执行,否则将状态更改为成功
        */
        if (this.status !== PENGDING) return;
        this.status = FULFILLED
        /*
           7、保存成功之后的值 ,then方法中调用失败回调(successCallback)的时候可以传递当前保存的this.value
        */
        this.value = value
        /*
            10、判断成功回调是否存在,如果存在则调用,
        */
        this.successCallback && this.successCallback(this.value)
    }
    reject = reason => {
        /*
            5、如果状态不是等待,阻止程序向下执行,否则将状态更改为失败
        */
        if (this.status !== PENGDING) return;
        this.status = REJECTED
        /*
          7、保存失败之后的原因,then方法中调用失败回调(failCallback)的时候可以传递当前保存的this.reason
       */
        this.reason = reason
        /*
           10、判断失败回调是否存在,如果存在则调用,
       */
        this.failCallback && this.failCallback(this.reason)
    }
    // 6、then方法 判断状态
    then(successCallback, failCallback) {
        if (this.status === FULFILLED) {
            successCallback(this.value)
        } else if (this.status === REJECTED) {
            failCallback(this.reason)
        } else {
            // 9、这里我们处理的就是当我们处于pending 状态的时候也就是出现异步情况的时候的.首先我们把成功回调和失败回调存储起来,等异步代码执行完毕之后才执行这个成功/失败回调 -然后我们就要找成功resolve-失败reject的回调
            this.successCallback = successCallback
            this.failCallback = failCallback
        }
    }
}


//调用
let promise = new MyPromise((resolve, reject) => {
  resolve('成功')
    // reject('失败')
})

promise.then(value => {
    console.log(value)
 }, reason => { 
     console.log(reason)
 })

三、当Promise 类中加入异步逻辑

let promise = new MyPromise((resolve, reject) => {
    // 假设当出现异步情况
    setTimeout(()=>{
        resolve('成功')
    },2000)
    // reject('失败')
})

promise.then(value => {
    console.log(value)
 }, reason => { 
     console.log(reason)
 })

1、 出现异步情况的实现办法

1 当出现`setTimeOut` 或者其他异步的情况的时候,导致代码没有直接调用`resolve`/`reject` 函数,此时 状态是`pengding` 所以按照代码运行顺序会直接进入到`then`方法,`then`方法会先执行,但是此时因为状态一直是`pengding` 我们并不知道是调用`successCallBack` 还是failCallBack,

2 ------------所以我们先将其存储(代码注释①)起来,当异步代码执行完毕之后我们才会执行resolve/reject,然后去判断成功/失败回调是否存在,若存在则调用它(代码注释②)。

const PENGDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        executor(this.resolve, this.reject)
    }
    status = PENGDING
    value = undefined
    reason = undefined    
    successCallback = undefined
    failCallback = undefined
    resolve = value => {
        if (this.status !== PENGDING) return;
        this.status = FULFILLED
        this.value = value
           /*  注释② */ 
        this.successCallback && this.successCallback(this.value)
    }
    reject = reason => {
        if (this.status !== PENGDING) 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 {
            /*  注释①
            - 这里处理的就是当处于pending ,也就是出现异步情况的时候的:
            - 首先我们把成功回调和失败回调存储起来,
            - 等异步代码执行完毕之后才执行这个成功/失败回调 
            - 然后我们就要找成功resolve-失败reject的回调
            */ 
            this.successCallback = successCallback
            this.failCallback = failCallback
        }
    }
}

四、实现 then 方法多次调用添加多个处理函数

then 方法被多次调用的时候,每一个then 方法中传递的回调函数都是要被执行的,这个时候我们要如何处理呢?

		
		let promise = new MyPromise((resolve, reject) => {
		    // 假设当出现异步情况
		    setTimeout(()=>{
		        resolve('成功')
		    },2000)
		    // reject('失败')
		})
		
		promise.then(value => {
		    console.log(value)
		 }, reason => { 
		     console.log(reason)
		 })
		 
		promise.then(value => {
		    console.log(value)
		 }, reason => { 
		     console.log(reason)
		 })
		 
		promise.then(value => {
		    console.log(value)
		 }, reason => { 
		     console.log(reason)
		 })

1、实现过程及分析

两种情况,一种是同步的情况 ,一种是异步的情况
下面我们就详细说明:每一个then 方法的回调函数都应该存储起来,当状态变为成功或者失败的时候,在依次调用里面的回调函数 上一个实例中出现异步的情况我们每次只能存储一个回调函数,所以我们要基于上面的情况再次进行处理

  1. 找到成功与失败回调设置默认值 由undefined 改为[]空数组,便于我们去存储多个回调函数
  2. 我们找到存储回调的地方将赋值改成 将我们的每个回调 push到我们的相应存储数组里面
  3. 当这个状态是成功还是失败的时候,这个时候我们应该去调用数组中的函数,这个函数如何调用呢?
    循环数组(while) 并调用
const PENGDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        executor(this.resolve, this.reject)
    }
    status = PENGDING
    value = undefined
    reason = undefined
    // successCallback = undefined
    // failCallback = undefined
    successCallback = []
    failCallback = []
    resolve = value => {
        if (this.status !== PENGDING) return;
        this.status = FULFILLED
        this.value = value
        // this.successCallback && this.successCallback(this.value)
        // shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
        while(this.successCallback.length>0) this.successCallback.shift()(this.value)
    }
    reject = reason => {
        if (this.status !== PENGDING) return;
        this.status = REJECTED
        this.reason = reason
        // this.failCallback && this.failCallback(this.reason)
        // shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
        while(this.failCallback.length>0) 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 = successCallback
            // this.failCallback = failCallback
            this.successCallback.push(successCallback)
            this.failCallback.push(failCallback)
        }
    }
}

打印结果

在这里插入图片描述

五、实现 then 方法的链式调用(一)-普通值

Promisethen 方法是可以链式调用的,后面then方法的回调函数拿到的值实质上是上一个then方法的回调函数的返回值
下面我们来演示一下链式调用返回的普通值的时候:

let promise = new MyPromise((resolve, reject) => {
    // 当出现异步情况
    // setTimeout(()=>{
    //     resolve('成功')
    // },2000)
    resolve('成功')
    // reject('失败')
})

promise.then(value => {
    console.log(value)
    return 100;
}).then(value => {
    console.log(value)  //100
})

###1、 实现方法及分析

下面我们通过两个步骤来实现

	const PENGDING = 'pending'
	const FULFILLED = 'fulfilled'
	const REJECTED = 'rejected'
	class MyPromise {
	    constructor(executor) {executor(this.resolve, this.reject)}
	    status = PENGDING
	    value = undefined
	    reason = undefined
	    successCallback = []
	    failCallback = []
	    resolve = value => {
	        if (this.status !== PENGDING) return;
	        this.status = FULFILLED
	        this.value = value
	        while(this.successCallback.length>0) 
	        this.successCallback.shift()(this.value)
	    }
	    reject = reason => {
	        if (this.status !== PENGDING) return;
	        this.status = REJECTED
	        this.reason = reason
	        while(this.failCallback.length>0) 
	        this.failCallback.shift()(this.reason) 
	    }
	    then(successCallback, failCallback) {
	        /* 
	         1. 既然我们要返回一个Promise 对象,那么我们创建一个Promise 对象,
	         2. 然后我们通过return 关键字 返回 promise2
	         3. 传递一个执行器,(立即执行)使原来的代码保持原有的功能
	         4. ---这样我们就实现了步骤一
	       */
	        let promise2 = new MyPromise((resolve,reject)=>{ 
	            if (this.status === FULFILLED) {
	              /* 实现步骤二
	               ①. 首先拿到函数的返回值(成功/失败)返回的promise2
				   ②. 把返回值传递给下一个then回调函数,调用promise2里的resolve方法
		         */
	              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
	    }
	}

- 返回实现结果
在这里插入图片描述

六、实现 then 方法的链式调用(一)-Promise对象

当我们以promise对象为返回值时

let promise = new MyPromise((resolve, reject) => {
    resolve('成功')
    // reject('失败')
})
// 先定义一个promise 对象
function otherPromise(params) {
    return new Promise((resolve,reject)=>{
        resolve('other')
    })
}
// 
promise.then(value => {
    console.log(value)
    return otherPromise();
}).then(value => {
    console.log(value)
})

1、当返回值是promise对象时实现方法

接下来我们要去判断 x 是普通值还是promise 对象
如果是普通值,直接resolve
如果是promise对象 查看promise对象返回的结果
在根据promise对象返回的结果(Promise 对象的状态),
若成功-----> 调用resolve 将成功的值传递给下一个
若失败-----> 调用reject 将失败的结果传递给下一个


const PENGDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        executor(this.resolve, this.reject)
    }
    status = PENGDING
    value = undefined
    reason = undefined
    successCallback = []
    failCallback = []
    resolve = value => {
        if (this.status !== PENGDING) return;
        this.status = FULFILLED
        this.value = value
        while (this.successCallback.length > 0) 
        this.successCallback.shift()(this.value)
    }
    reject = reason => {
        if (this.status !== PENGDING) return;
        this.status = REJECTED
        this.reason = reason
        while (this.failCallback.length > 0) 
        this.failCallback.shift()(this.reason)

    }
    then(successCallback, failCallback) {

        let promise2 = new MyPromise((resolve, reject) => {

            if (this.status === FULFILLED) {
                let x = successCallback(this.value)
                resolvePromise(x, resolve, reject)
                //   resolve(x)
            } else if (this.status === REJECTED) {
              failCallback(this.reason)
            } else {
                this.successCallback.push(successCallback)
                this.failCallback.push(failCallback)
            }
        });

        // 2然后我们通过return 关键字 返回promise2
        return promise2
    }
}

 // 判断x是普通值还是Promise对象
function resolvePromise(x, resolve, reject) {
    //  我们只需要判断x 是不是MyPromise 的这个实例
    if (x instanceof MyPromise) {
        // x.then(value => { resolve(value)}, reason => { reject(reason)})
        //简化代码
        x.then(resolve,reject)
    } else {
        resolve(x)
    }
}

七、then 方法链式调用识别 Promise 对象自返回

1、html模拟演示个例

我们新建一个index.html文件,输入以下代码,
模拟演示一下

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        var a = new Promise(function (resolve, reject) {
            resolve('100')
        })
        var p1 = a.then(function (value) {
            console.log(value)
            return  p1
        })
    </script>
</body>

</html>

查看当前系统结果 会报错
在这里插入图片描述

2、在当前promise中判断个例情况

首先我们调用的时候

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

3、Promise 对象自返回解决方法

   1、 找到then方法
   2、返回的promise对象就是promise2 ,
   	  成功的回调返回的promise对象就是x
   3、我们只需要判断promise对象和x是否相等,
      如果相等则就说明是自己返回了自己,
      出现问题了,我们就要执行reject,
   4、在resolvePromise中处理
const PENGDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) { executor(this.resolve, this.reject)}
    status = PENGDING
    value = undefined
    reason = undefined
    successCallback = []
    failCallback = []
    resolve = value => {
        if (this.status !== PENGDING) return;
        this.status = FULFILLED
        this.value = value
        while (this.successCallback.length > 0) this.successCallback.shift()(this.value)
    }
    reject = reason => {
        if (this.status !== PENGDING) return;
        this.status = REJECTED
        this.reason = reason

        while (this.failCallback.length > 0) this.failCallback.shift()(this.reason)

    }
    then(successCallback, failCallback) {

        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                // 异步代码原因:是此过程执行完在执行词代码 是promise2 可以传值不会报错
                setTimeout(()=>{
                    let x = successCallback(this.value)
                    resolvePromise(promise2,x, resolve, reject)
                },0)
            } else if (this.status === REJECTED) {
              failCallback(this.reason)
            } else {
                this.successCallback.push(successCallback)
                this.failCallback.push(failCallback)
            }
        });
        return promise2
    }
}


function resolvePromise(promise2,x, resolve, reject) {
    // 如果相等,就说明自己反悔了自己,捕捉到了问题,提示错误
  if(promise2===x){
    //   使用return 是出现问题程序报错后阻止代码向下执行
     return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
    if (x instanceof MyPromise) {
       
        x.then(resolve,reject)
    } else {
        resolve(x)
    }
}

执行结果:
我们就可以直接查看我们捕捉的错误,就可以啦~
在这里插入图片描述

八、捕获错误及then链式调用其他状态代码

上述的所有我们没有对任何错误情况进行捕捉,下面我们对一些错误进行捕捉

1、 执行器错误

	当执行器中的代码在执行的过程当中发生错误的时候,
	我们就要把执行的状态变成是失败的状态
let promise = new MyPromise((resolve, reject) => {
    // 我们抛出错误演示一下
    throw new Error('执行器错误')
    resolve('成功')
    // reject('失败')
})
promise.then(value => {
    console.log(value)
},reason=>{
    console.log(reason,‘=’)
})
	捕捉异常处理,我们只需要在上述代码中在执行器处 
	通过try ,catch来捕获异常,调用reject方法打印错误
 constructor(executor) {
        // 执行器中捕捉错误
        try{
            executor(this.resolve, this.reject)
        } catch (e){
            // 如果捕捉到了错误 我们把错误原因e传递过去
            this.reject(e)
        }
        // executor(this.resolve, this.reject)
    }

结果显示
在这里插入图片描述

2、 then方法里面的回调函数错误

then方法里面的回调函数,若在执行过程中报错了,
下面我们演示一下回调函数执行中异常并抛出错误。
let promise = new MyPromise((resolve, reject) => {
    resolve('成功')
    // reject('失败')
})
promise.then(value => {
    console.log(value)
       // 我们抛出错误演示一下
    throw new Error('回调函数错误')
},reason=>{
    console.log(reason)
}).then(value => {
    console.log(value)
},reason=>{
    console.log(reason.message)
})

捕捉到这个错误,并且这个错误要在下一个then方法的错误回调reject中捕捉到,并返回
then(successCallback, failCallback) {
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(()=>{
                    // then方法里面的回调函数中捕捉错误
                    try{
                        let x = successCallback(this.value)
                        resolvePromise(promise2,x, resolve, reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
            } else if (this.status === REJECTED) {
              failCallback(this.reason)
            } else {
                this.successCallback.push(successCallback)
                this.failCallback.push(failCallback)
            }
        });
        return promise2
    }

实现结果
在这里插入图片描述

3、链式调用捕捉错误结果返回

我们之前处理链式调用 只处理了成功的状态 并没有处理失败的状态,
那么当失败的时候或者当代码是异步的时候都没有处理

模拟演示实现错误调用

let promise = new MyPromise((resolve, reject) => {
    // resolve('成功')
    reject('失败')
})
promise.then(value => {
    // console.log(value)
},reason=>{
    console.log(reason)
    return "失败"
}).then(value => {
    console.log(value,'捕捉错误回调')
})

捕捉代码处理,在then方法中

   then(successCallback, failCallback) {
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(()=>{
                    // then方法里面的回调函数中捕捉错误
                    try{
                        let x = successCallback(this.value)
                        resolvePromise(promise2,x, resolve, reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
            } else if (this.status === REJECTED) {
              // then方法里面的失败函数中捕捉错误
              setTimeout(()=>{
                try{
                    let x = failCallback(this.reason)
                    resolvePromise(promise2,x, resolve, reject)
                }catch(e){
                    reject(e)
                }
            },0)
            } else {
                this.successCallback.push(successCallback)
                this.failCallback.push(failCallback)
            }
        });
        return promise2
    }

执行结果
在这里插入图片描述

4、当代码是异步的情况捕捉错误


let promise = new MyPromise((resolve, reject) => {
    setTimeout(()=>{
        resolve('成功....')
    },2000)
    // resolve('成功')
    // reject('失败')
})
promise.then(value => {
    console.log(value)
    return 'aaaa'
},reason=>{
    console.log(reason)
}).then(value => {
    console.log(value,'捕捉错误回调')
})
const PENGDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(executor) {
        try{
            executor(this.resolve, this.reject)
        } catch (e){
            this.reject(e)
        }
    }
    status = PENGDING
    value = undefined
    reason = undefined
    successCallback = []
    failCallback = []
    resolve = value => {
        if (this.status !== PENGDING) return;
        this.status = FULFILLED
        this.value = value
        // 当处理异步 捕捉错误的时候这个手已经不需要传值了,
        while (this.successCallback.length > 0) this.successCallback.shift()()
    }
    reject = reason => {
        if (this.status !== PENGDING) return;
        this.status = REJECTED
        this.reason = reason
 // 当处理异步 捕捉错误的时候这个手已经不需要传值了,
        while (this.failCallback.length > 0) this.failCallback.shift()()
    }
    then(successCallback, failCallback) {
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(()=>{
                    // then方法里面的回调函数中捕捉错误
                    try{
                        let x = successCallback(this.value)
                        resolvePromise(promise2,x, resolve, reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
            } else if (this.status === REJECTED) {
            //   failCallback(this.reason)
              setTimeout(()=>{
                // then方法里面的回调函数中捕捉错误
                try{
                    let x = failCallback(this.reason)
                    resolvePromise(promise2,x, resolve, reject)
                }catch(e){
                    reject(e)
                }
            },0)
            } else {
                this.successCallback.push(()=>{
                    setTimeout(()=>{
                        // then方法里面的回调函数中捕捉错误
                        try{
                            let x = successCallback(this.value)
                            resolvePromise(promise2,x, resolve, reject)
                        }catch(e){
                            reject(e)
                        }
                    },0)
                })
                this.failCallback.push(()=>{
                    setTimeout(()=>{
                        // then方法里面的回调函数中捕捉错误
                        try{
                            let x = failCallback(this.reason)
                            resolvePromise(promise2,x, resolve, reject)
                        }catch(e){
                            reject(e)
                        }
                    },0)
                })
            
            }
        });
        return promise2
    }
}

实现结果 2s后打印
在这里插入图片描述


九、链式调用时将then方法的参数变成可选参数

处理方法

// 在then 方法中添加
   successCallback = successCallback ? successCallback : value => value;
   failCallback = failCallback ? failCallback : reason => { throw reason }

//成功调用

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

// 失败调用

let promise = new MyPromise((resolve, reject) => {
    reject('失败')
})
promise.then().then().then(value=>console.log(value),reason=>{console.log(reason)})

以上内容均为个人学习笔记,不存在任何其他或者商业行为 ,如有侵权或者其他,必删除。请私聊或者评论告知。

标签:resolve,failCallback,会学,successCallback,value,reason,api,reject,源码
来源: https://blog.csdn.net/weixin_43405717/article/details/117709048