JavaScript异步编程【中】 -- Promise 详细解析

文章内容输出来源:拉勾教育 大前端高薪训练营

前言

在ES6中,新增加了一种异步编程的解决方案Promise,它是一种规范,是一套处理JavaScript异步的机制

Promise的含义

简单来说,它是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,通过它可以获取异步操作的消息。

Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。

Promise 对象有两个特点:

1、对象的状态不受外界影响。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
2、一旦状态改变,就不会再变,任何时候都可以得到这个结果。

Promise对象的状态改变,只有两种可能:
* 从 pending 变为 fulfilled;
* 从 pending 变为 rejected。

Promise的基础用法

Promise是一个类,它的构造函数中需要一个执行器作为参数传入进去,当使用Promise创建promise实例时,执行器会立即执行。在执行器中需要两个函数作为参数传递,即 resolve 和 reject 函数,这两个函数主要是用来更改状态的。
resolve 函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject 函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

  • 类核心逻辑的实现

下面我们来模拟一下Promise的类核心逻辑的实现。

代码如下(示例):(promise.js)

// 定义Promise构造函数

// 定义promise的三种状态
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class Promise {
    
    
	// 传入执行器, 创建实例时,立即执行
	constructor (executor) {
    
    
		executor(this.resolve, this.reject);
	}
	status = PENDING;    // 初始化状态 为 pending  等待
    value = undefined;   // 保存成功之后的值
    reason = undefined;  // 保存失败后的原因
    resolve = value => {
    
    
        // 如果状态不是等待,阻止程序向下执行
        if (this.status !== PENDING) return
        // 将状态更改为成功 pending --> fulfilled
        this.status = FULFILLED
        // 保存成功之后的值, 传入的值
        this.value = value
    }
	reject = reason => {
    
    
		// 如果状态不是等待,阻止程序向下执行
        if (this.status !== PENDING) return;
        // 将状态更改为失败 pending --> rejected
        this.status = REJECTED;
        // 保存失败后的原因,传入的原因
        this.reason = reason;
	}
}
module.exports = Promise;

创建实例 代码如下(示例):(index.js)

扫描二维码关注公众号,回复: 12464695 查看本文章
// 引入 promise 模块
const Promise = require('./promise')
// 创建 Promise实例,返回 promise对象
let promise = new Promise((resolve, reject) => {
    
    
    resolve(111);
})
console.log(promise);	
/*
	Promise:{
	     status: 'fulilled',
	     value: 111,
	     reason: undefined, 
	     resolve: [Function: resolve],
	     reject: [Function: reject]
	}
*/ 

在上面的代码中,我们已经通过代码创建了 Promise构造函数,并通过调用构造函数创建了 promise实例,且返回了一个 promise对象。那么,实例创建成功以后,我们如何指定resolved状态和rejected状态的回调函数呢?

Promise.prototype.then()

在 Promise的原型对象中定义了一个then方法,then方法主要做的事情就是判断状态,如果状态是成功的,那么调用成功的回调函数;如果状态是失败的,那么调用失败的回调函数。而要调用then方法,必须返回一个 promise对象,在上面的代码中,我们已经实现了返回 promise对象。 then方法有两个参数:一个是成功回调函数,它接收一个参数,表示成功之后的值;二是失败回调函数,它也接收一个参数,表示失败后的原因。

下面让我们来看一下 then方法 的基础实现过程。

代码如下(示例):(promise.js)

// 重复代码以下使用 ...... 代替
......
class Promise {
    
    
	......
	then (successCallback, failCallback) {
    
    
		if (this.status === FULFILLED) {
    
    
			// 成功回调函数,将传入resolve函数中传入的值进行传入
			successCallback(this.value)  
		} else if (this.status === REJECTED) {
    
    
			// 失败回调函数,将传入reject函数中传入的原因进行传入
			failCallback(this.reason)
		}
	}
}

调用then方法 代码如下(示例):(index.js)

let promise = new Promise((resolve, reject) => {
    
    
    resolve(111);
})
promise.then(value => console.log(value), reason => console.log(reason))
// 打印:1111

在上面的代码中,由于我们在创建实例时,抛出的是成功的函数resolve,所以此时,then方法中执行的是成功的回调函数,此时value的值为resolve中保存的值,即value = 111,所以打印出111。

自此,我们已经完成了基本的 Promise的类核心逻辑。但是我们只是进行了同步的操作,并没有引入异步的逻辑,而在前面我们也讲到Promise主要就是为了解决异步操作的。

那么,我们接下来看一下,Promise是如何处理异步操作的?

代码如下(示例):(promise.js)

......
class Promise {
    
    
	......
	// 成功回调
    successCallback = undefined
    // 失败回调
    failCallback = undefined
    resolve = value => {
    
    
		......
        // 此时是只调用一次then方法的写法
        // 判断成功回调是否存在 如果存在 调用
        this.successCallback && this.successCallback(this.value)
	}
	
	reject = reason => {
    
    
		......
        // 此时是只调用一次then方法的写法
		// 判断失败回调是否存在 如果存在 调用
        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 {
    
     // 等待期 
		   // 处理异步操作
		   /* 添加定时器(异步操作),导致 promise的状态进入等待期。
		   	  1、如果我们不将成功回调和失败回调存储起来,那么定时器中的resolve(100)或者reject('error'),将不会被执行;
              2、需要在resolve和reject函数中判断成功回调和失败回调是否存在,存在时才会被调用
            */
            // 此时是只调用一次then方法的写法
            this.successCallback = successCallback; 
            this.failCallback = failCallback;
        }
	}
}

代码如下(示例):(index.js)

let promise = new Promise((resolve, reject) => {
    
    
    setTimeout(() => {
    
    
        resolve(111)
        // reject('error')
    }, 2000)
})
promise.then(value => console.log(value), reason => console.log(reason))
console.log(promise); // 此时处于等待期
// console.log(promise) 是处于主线程中的方法,会立即执行, 因此,打印顺序如下
/* -- 打印结果:
	Promise:{
	     status: 'pending',
	     value: undefined,
	     reason: undefined, 
	     successCallback: [Function],   
	     failCallback: [Function],
	     resolve: [Function: resolve],
	     reject: [Function: reject]
	}
	111
*/

在上面的代码中,我们是通过将成功回调和失败回调存储起来的方法,来解决Promise中的异步操作的。在上述代码中,我们只调用了一次 then方法,但其实同一个promise对象下面的then方法是可以被多次调用添加多个处理函数的。并且,当then方法被多次调用时,每一个then方法都是要被执行的。

代码如下(示例):(promise.js)

......
class Promise {
    
    
	......
    successCallback = [] // 多次调用,储存多个成功回调函数
    failCallback = []    // 多次调用,储存多个失败回调函数
    resolve = value => {
    
    
		......
        // 此时是多次调用then方法的写法
        while (this.successCallback.length) {
    
    
			this.successCallback.shift()(this.value)
		}
	}
	
	reject = reason => {
    
    
		......
        // 此时是多次调用then方法的写法
        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 {
    
     // 等待期 
		   	// 处理异步操作
        	/*
        		当我们多次调用then方法时,我们应该将每一次的成功回调和失败回调都储存起来
        	    因此,我们应该使用数组的形式,储存每一次调用then方法后指定的成功回调和失败回调
        	*/
        	// 此时多次调用then方法的写法
            this.successCallback.push(successCallback);
            this.failCallback.push(failCallback);
        }
	}
}

代码如下(示例):(index.js)

......
// 调用三次 then方法
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))

console.log(promise); // 此时处于等待期
/* -- 打印结果:
	Promise:{
	     status: 'pending',
	     value: undefined,
	     reason: undefined, 
	     successCallback: [ [Function], [Function], [Function] ],   
	     failCallback: [ [Function], [Function], [Function] ], 
	     resolve: [Function: resolve],
	     reject: [Function: reject]
	}
	111
	111
	111
*/

在上面的代码中,我们展示了同一个 Promise对象调用多次 then方法添加多个处理函数的情况。但是有时在实际开发中,我们在调用下一个then方法时,我们需要借助上一个then方法中的返回值,这有点类似我们在上面所说的回调嵌套,在上面我们说到,如果使用回调函数的写法,很容易导致 回调地域 的情况的发生。那么,我们在 Promise中是否有办法解决这个问题呢?
其实,在 Promise是提供了这样的功能的,就是利用 then方法的链式调用。下面,我们就来具体的看一下 then方法的链式调用的实现。

  • 为了节省篇幅,以下代码,只分析 成功时的情况,最后会补全失败和等待期的代码

  • 首先,我们先来看一下 then方法中值传递的情况。

代码如下(示例):(promise.js)

......
class Promise {
    
    
	......
	/* -- 逻辑分析:
	      1、调用 then方法的,必须是一个 Promise对象,因此在执行then方法后,需要返回一个promise对象;
		  2、我们需要把上一个then方法中的回调函数的返回值传递给下一层then方法的回调函数, 
		     因此我们需要,调用执行完上一个then方法后,返回的新的 promise2对象中的执行器中的resolve函数             
	*/
	then (successCallback, failCallback) {
    
    
		// 1. 实现了 让then方法返回 promise对象
		// 传入执行器, 立即执行
        let promise2 = new Promise((resolve, reject) => {
    
     
            if (this.status === FULFILLED) {
    
    
        // 2. 实现把上一个then方法中的回调函数的返回值传递给下一层then方法的回调函数                
               let x = successCallback(this.value)   // x = 111
               // x 为成功回调函数的返回值,并将其传递给下一层then方法的回调函数中
               // 调用 resolve(x)方法,把上一层then方法返回的值传递给下层then方法的成功回调函数
               resolve(x)
            } else if (this.status === REJECTED) {
    
    
                // 失败回调函数,将传入reject函数中传入的原因进行传入
                failCallback(this.reason)
            } else {
    
    
                // 等待期
                this.successCallback.push(successCallback);
                this.failCallback.push(failCallback);
            }

        })
        return promise2;
	}
}

代码如下(示例):(index.js)

let promise = new Promise((resolve, reject) => {
    
    
    resolve(111);
})
promise.then(value => {
    
    
    console.log(value)    // 111
    return 1;
}).then(value => {
    
     
    console.log(value) // 1
    return value
}).then(value => console.log(value))   // 1
  .then(value => console.log(value))   // undefined 

注意 要想实现 then方法的链式调用,上一层的成功回调函数必须有 return 返回值,否则,下层then方法是获取不到上层then方法中的返回值的。

由于在成功函数和失败函数以及等待期时,执行的操作应该是相同的操作,那么,在下面我们将单独封装一个函数,实现代码重用,减少代码冗余。

  • 下面我们再来看一下,then方法中返回 promise对象的情况。

代码如下(示例):(promise.js)

......
class Promise {
    
    
	......
	then (successCallback, failCallback) {
    
    
		// 1. 实现了 让then方法返回 promise对象
        let promise2 = new Promise((resolve, reject) => {
    
     
            if (this.status === FULFILLED) {
    
                   
               let x = successCallback(this.value)  
               // 判断 x 的值是 普通值 还是 promise对象 封装函数
               resolvePromise(x, resolve, reject)
            } else if (this.status === REJECTED) {
    
                   
                failCallback(this.reason)
            } else {
    
    
                // 等待期
                this.successCallback.push(successCallback);
                this.failCallback.push(failCallback);
            }

        })
        return promise2;
	}
}

function resolvePromise (x, resolve, reject) {
    
    
    /** -- 逻辑分析
     * 判断 x 的值是 普通值 还是 promise对象
     * 如果是普通值,直接调用 resolve
     * 如果是 promise对象 查看 promise对象返回的结果
     * 再根据 promise 对象返回的结果 决定调用 resolve  还是调用 reject
     */
    if (x instanceof Promise) {
    
     // x 是promise的情况
        /* x 的打印情况
			Promise:{
			     status: 'fulfilled',
			     value: 'new promise',
			     reason: undefined, 
			     successCallback: [ [Function], [Function], [Function] ],   
			     failCallback: [ [Function], [Function], [Function] ], 
			     resolve: [Function: resolve],
			     reject: [Function: reject]
			}
		*/
		// x.then(value => resolve(value), reason => reject(reason))
        x.then(resolve, reject) // 代码简化
    } else {
    
     // x 是 值 的情况
        resolve(x)
    }
}

代码如下(示例):(index.js)

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

在上面的代码中,我们是单独创建了一个新的 promise对象进行返回的。但我们在使用的时候可以看到,如果返回当前的 promise对象,会出现下面的错误。

代码如下(示例):(test.html)

 let promise = new Promise(function (resolve, reject) {
    
     resolve(100) });
 let p1 = promise.then(value => {
    
    
     console.log(value);
     return p1; 
 }) 
 p1.then(value => console.log(value) }, reason => console.log(reason))

报错信息:

在这里插入图片描述
那么,我们该如何识别 promise对象自返回呢?

代码如下(示例):(promise.js)

......
class Promise {
    
    
	......
	then (successCallback, failCallback) {
    
    
        let promise2 = new Promise((resolve, reject) => {
    
     
            if (this.status === FULFILLED) {
    
       
            /* 
                由于这里属于程序运行过程中,而promise2是在程序执行完成后创建的,
                因此,我们可以将下面的代码使用定时器转换为异步代码。
                由于我们使用定时器只是为了转换,不是为了延时,因此我们需要将时间设为0
            */
               setTimeout(() => {
    
                
	               let x = successCallback(this.value)  
	               resolvePromise(promise2, x, resolve, reject)
               }
            } 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) {
    
    
    // 判断是否是自己调用自己, 如果识别了 promise对象在自返回,那么我们模拟需要抛出下面的错误,并使用 return 阻止程序往下运行
    if (promise2 === x) {
    
    
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>')); 
    }
    ......
}

在上面的代码中,我们的代码其实并不完善,很多可能发生的错误并没有被捕获,那么下面我们来对代码进行一下补充和完善,使它用起来更加健壮。

代码如下(示例):(promise.js)

// 定义promise的三种状态
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class Promise {
    
    
    constructor(executor) {
    
    
        // 捕获执行器的错误
        try {
    
    
            executor(this.resolve, this.reject);
        } catch (e) {
    
    
            this.reject(e)
        }
    }
    status = PENDING;
    value = undefined;
    reason = undefined;
    successCallback = [];
    failCallback = [];
    resolve = value => {
    
    
        if (this.status !== PENDING) return
        this.status = FULFILLED
        this.value = value
        while (this.successCallback.length) {
    
    
            this.successCallback.shift()() // 在成功回调函数中已经进行了操作,因此就不用再传值了
        }
    }
    reject = reason => {
    
    
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason;
        while (this.failCallback.length) {
    
    
            this.failCallback.shift()() // 在失败回调函数中已经进行了操作,因此就不用再传值了
        }
    }

    then(successCallback, failCallback) {
    
    
        let promise2 = new Promise((resolve, reject) => {
    
    
            if (this.status === FULFILLED) {
    
                    
                setTimeout(() => {
    
    
                    try {
    
        
                        let x = successCallback(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
    
    
                        reject(e)
                    }
                }, 0)

            } else if (this.status === REJECTED) {
    
    
                setTimeout(() => {
    
    
                    try {
    
                    
                        let x = failCallback(this.reason)
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
    
    
                        reject(e)
                    }
                }, 0)

            } else {
    
    
                // 等待期
                this.successCallback.push(() => {
    
    
                    setTimeout(() => {
    
    
                        try {
    
    
                            let x = successCallback(this.value)
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
    
    
                            // 调用下一个promise的reject回调函数
                            reject(e);
                        }
                    }, 0)
                })
                this.failCallback.push(() => {
    
    
                    setTimeout(() => {
    
    
                        try {
    
    
                            let x = failCallback(this.reason)
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
    
    
                            reject(e);
                        }
                    }, 0)
                })
            }

        })
        return promise2;
    }

}

function resolvePromise(promise2, x, resolve, reject) {
    
    
    if (promise2 === x) {
    
    
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
    }

    if (x instanceof Promise) {
    
    
        x.then(resolve, reject)
    } else {
    
    
        resolve(x)
    }
}

module.exports = Promise;

代码如下(示例):(index.js)

const Promise = require('./promise')
let promise = new Promise((resolve, reject) => {
    
    
    setTimeout(() => {
    
    
        resolve(111)
    }, 2000)
    // throw new Error('executor error')
    // resolve(111)
    // reject('error')
})
promise.then(value => {
    
    
    console.log(value);   // 2秒以后,111
    return 'aaa';  
}, reason => {
    
    
    console.log(reason); 
    // throw new Error('then error')
    return 111;
}).then(value => console.log(value))  // 'aaa'

截至到目前为止,我们已经完成了 Promise类的核心逻辑,但是在上面所写的 then方法中,我们所传入的参数是必须要传入的,否则后面执行的then方法是无法获取到值的。

代码如下(示例):(index.js)

let promise = new Promise((resolve, reject) => {
    
    
    resolve(111)
})
promise.then().then(value => console.log(value))  // 未进行值传递,没有打印

那么,我们如何将 then方法中的参数变成可选参数,以便当不传递参数时,promise状态会依次向下传递,直到传递给有回调函数的then方法?

代码如下(示例):(promise.js)

......
class Promise {
    
    
	......
	then (successCallback, failCallback) {
    
    
		// 判断successCallback是否存在,
		// 如果存在时,等于原值;如果不存在,则将值往下进行传递
	 	// failCallback 同理
		successCallback = successCallback ? successCallback : value => value; 
		failCallback = failCallback ? failCallback : reason => reason; 
		......
	}
}

代码如下(示例):(index.js)

let promise = new Promise((resolve, reject) => {
    
    
    resolve(111)
})
promise.then().then(value => console.log(value))  // 进行值传递,打印:111

讲到这里,我们已经实现了 Promise的then方法,那么下面我们来介绍一个处理并发问题的API。

Promise.all()

Promise.all()用于将多个 Promise 实例,包装成一个新的 Promise 实例,并且作为返回值返回,且可以链式调用then方法。Promise.all() 允许异步代码按照异步代码调用的顺序,得到结果。并且接收一个数组作为参数,数组中元素的顺序一定是我们得到的结果的顺序。

特点:

1、在all方法中所有的promise对象的状态都是成功的,那么最后的结果就是成功的;
2、在all方法中存在一个promise对象的状态是失败的,那么最后的结果就是失败的。

下面,我们来模拟一下 all 方法的实现过程。

代码如下(示例):(promise.js)

......
class Promise {
    
    
	......
	 static all(array) {
    
    
        // 一、首先需要返回一个 promise对象实例,并且传入一个生成器,立即执行
        let result = []; // 结果数组
        let index = 0;
        return new Promise((resolve, reject) => {
    
    
            /* 逻辑分析
                二、我们需要对传入的数组进行遍历
                三、需要判断数组中的每一项元素是 promise对象 还是普通值
                    1、如果是普通值,那么直接放到结果数组中;
                    2、如果是 promise对象,则需要先执行这个 promise对象,然后再把返回的结果放到结果数组中
            */
            for (let i = 0; i < array.length; i++) {
    
    
                let current = array[i];
                if (current instanceof Promise) {
    
     // 传入的是 promise对象
                    // 可以链式调用then方法
                    current.then(value => addData(i, value), reason => reject(reason))
                } else {
    
     // 普通值
                    addData(i, array[i])
                }
            }
            // 给结果数组赋值
            function addData (key, value) {
    
    
                result[key] = value;                
                /* 
                    四、由于for循环的执行是很快的,如果传入的 promise对象中含有异步操作,
                    那么很有可能结果数组中会出现空值的情况 
                    即 [ '1', '2', <1 empty item>, 'p2' ]
                */
                index++;
                // 等待所有异步操作都完成,才要执行resolve
                if (index === array.length) {
    
    
                    resolve(result) // 结果数组进行赋值时,才会调用resolve, 使之执行顺序和传入顺序一致
                }
            }
        })
    }
}

代码如下(示例):(index.js)

function p1 () {
    
    
    return new Promise((resolve, reject) => {
    
    
        setTimeout(() => {
    
    
            resolve('p1')
        }, 2000)
    })
}
function p2 () {
    
    
    return new Promise((resolve, reject) => {
    
    
        resolve('p2')
    })
}

Promise.all(['1', '2', p1(), p2(), 3]).then(value => {
    
    
    console.log(value);   // [ '1', '2', 'p1', 'p2', 3 ]
})

在上面的代码中,我们都是自己创建的 promise对象。那么,有没有一个方法可以将普通的值或对象等转换成 promise对象呢?
其实,在Promise类中是提供了这样一个方法的,即 Promise.resolve()。

Promise.resolve()

Promise.resolve()的作用,就是将给定的值转换成 promise对象,也就是说,返回值就是一个 promise对象。

接下来我们来看一下,resolve()方法的代码实现过程。

代码如下(示例):(promise.js)

...... 
class Promise {
    
    
	......
	static resolve (value) {
    
    
        /*
            判断 value 是 promise对象,还是普通值
            如果是 promise对象,则直接返回
            如果是 普通值,使用resolve将传入的值进行包裹,然后返回这个新建的 promise对象
        */ 
       if (value instanceof Promise) {
    
    
           return value;
       } else {
    
    
           return new Promise(resolve => resolve(value));
       }
    }
}

代码如下(示例):(index.js)

function p2 () {
    
    
    return new Promise((resolve, reject) => {
    
    
        resolve('p2')
    })
}

Promise.resolve(1).then(value => console.log(value)) // 1
Promise.resolve(p2()).then(value => console.log(value)) // p2

Promise.prototype.finally()

finally 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。

  • 特点

1、无论 promise对象最终的状态是成功的还是失败的,finally方法中的回调函数始终都会被执行一次;
2、可以在 finally方法的后面链式调用then方法,拿到这个 promise对象最终返回的结果。

代码如下(示例):(promise.js)

..... 
class Promise {
    
    
	......
 	finally (callback) {
    
    
        /* 逻辑分析
            无论当前promise的状态成功还是失败,都要执行callback这个回调函数
            首先,要获取当前 promise对象的状态,可以通过then方法获取;
            然后,需要在finally方法的后面链式调用then方法,要返回一个 promise对象
                  因此可以直接返回then方法返回的 promise对象;
            接着,需要再次调用then方法,需要拿到当前then方法中promise对象的结果,并将其return
            最后,如果promise对象返回的结果中包含异步逻辑,
                  而此时then方法可能不会等到异步操作执行完成,就直接执行了
                  那么需要使用 Promise.resolve()方法,查看一下callback中的返回值,是普通值 还是 promise对象, 无论是什么,都等待其完成
        */
        return this.then(value => {
    
    
            return Promise.resolve(callback()).then(() => value);
        }, reason => {
    
    
            // 传递错误原因
            return Promise.resolve(callback()).then(reason => {
    
     throw reason });
        })
    }
    ......
}

代码如下(示例):(index.js)

function p1 () {
    
    
    return new Promise((resolve, reject) => {
    
    
        setTimeout(() => {
    
    
            resolve('p1')
        }, 2000)
    })
}
function p2 () {
    
    
    return new Promise((resolve, reject) => {
    
    
        resolve('p2')
    })
}
p2().finally(() => {
    
    
    console.log('finally');
    return p1();  
    // return 100;
}).then(value => {
    
    
     console.log(value);
}, reason => {
    
    
     console.log(reason);
})

Promise.prototype.catch()

catch方法用来处理当前 promise对象最终的状态为失败的情况的,内部注册的只有失败回调,成功回调返回undefined。

代码如下(示例):(promise.js)

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

代码如下(示例):(index.js)

function p2 () {
    
    
    return new MyPromise((resolve, reject) => {
    
    
        resolve('p2')
    })
}
p2().then(value => console.log(value))
	.catch(reason => console.log(reason))

总结

在上面的代码中,我们详细分析了Promise 底部核心逻辑的实现过程。最后在此附上全部的代码,希望对看到这篇文章的朋友们有所帮助。

代码如下(示例):(promise.js)

// 定义promise的三种状态
const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败
class Promise {
    
    
    constructor(executor) {
    
    
        try {
    
    
            executor(this.resolve, this.reject);
        } catch (e) {
    
    
            this.reject(e)
        }
    }
    status = PENDING;
    value = undefined;
    reason = undefined;
    successCallback = [];
    failCallback = [];
    resolve = value => {
    
    
        if (this.status !== PENDING) return
        this.status = FULFILLED
        this.value = value
        while (this.successCallback.length) {
    
    
            this.successCallback.shift()() 
        }
    }
    reject = reason => {
    
    
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason;
        while (this.failCallback.length) {
    
    
            this.failCallback.shift()() 
        }
    }

    then(successCallback, failCallback) {
    
    
        successCallback = successCallback ? successCallback : value => value;
        failCallback = failCallback ? failCallback : reason => reason;
        let promise2 = new Promise((resolve, reject) => {
    
    
            if (this.status === FULFILLED) {
    
    
                setTimeout(() => {
    
    
                    try {
    
    
                        let x = successCallback(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
    
    
                        reject(e)
                    }
                }, 0)

            } else if (this.status === REJECTED) {
    
    
                setTimeout(() => {
    
    
                    try {
    
    
                        let x = failCallback(this.reason)
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
    
    
                        reject(e)
                    }
                }, 0)

            } else {
    
    
                this.successCallback.push(() => {
    
    
                    setTimeout(() => {
    
    
                        try {
    
    
                            let x = successCallback(this.value)
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
    
    
                            reject(e);
                        }
                    }, 0)
                })
                this.failCallback.push(() => {
    
    
                    setTimeout(() => {
    
    
                        try {
    
    
                            let x = failCallback(this.reason)
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
    
    
                            reject(e);
                        }
                    }, 0)
                })
            }

        })
        return promise2;
    }

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

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

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

function resolvePromise(promise2, x, resolve, reject) {
    
    
    if (promise2 === x) {
    
     
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
    }
    if (x instanceof Promise) {
    
    
        x.then(resolve, reject)
    } else {
    
    
        resolve(x)
    }
}
module.exports = Promise;

猜你喜欢

转载自blog.csdn.net/zimeng303/article/details/109360305