Promise的特性及实现原理

Promise对象的特性

要实现Promise对象首先我们要了解Promise拥有哪些特性,简单概括为以下几点
1、Promise有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)
2、Promise对象接受一个回调函数作为参数, 该回调函数接受两个参数,分别是成功时的回调resolve和失败时的回调reject;另外resolve的参数除了正常值以外, 还可能是一个Promise对象的实例;reject的参数通常是一个Error对象的实例。
3、then方法返回一个新的Promise实例,并接收两个参数onResolved(fulfilled状态的回调);
onRejected(rejected状态的回调,该参数可选)
4、catch方法返回一个新的Promise实例
5、finally方法不管Promise状态如何都会执行,该方法的回调函数不接受任何参数
6、Promise.all()方法将多个多个Promise实例,包装成一个新的Promise实例,该方法接受一个由Promise对象
组成的数组作为参数(Promise.all()方法的参数可以不是数组,但必须具有Iterator接口,且返回的每个成员都是Promise实例),注意参数中只要有一个实例触发catch方法,都会触发Promise.all()方法返回的新的实例的catch方法,如果参数中的某个实例本身调用了catch方法,将不会触发Promise.all()方法返回的新实例的catch方法
7、Promise.race()方法的参数与Promise.all方法一样,参数中的实例只要有一个率先改变状态就会将该实例的状态传给Promise.race()方法,并将返回值作为Promise.race()方法产生的Promise实例的返回值
8、Promise.resolve()将现有对象转为Promise对象,如果该方法的参数为一个Promise对象,Promise.resolve()将不做任何处理;如果参数thenable对象(即具有then方法),Promise.resolve()将该对象转为Promise对象并立即执行then方法;如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的Promise对象,状态为fulfilled,其参数将会作为then方法中onResolved回调函数的参数,如果Promise.resolve方法不带参数,会直接返回一个fulfilled状态的 Promise 对象。需要注意的是,立即resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。

setTimeout(function () { console.log('three'); }, 0); Promise.resolve().then(function () { console.log('two'); }); console.log('one'); // one // two // three 

9、Promise.reject()同样返回一个新的Promise对象,状态为rejected,无论传入任何参数都将作为reject()的参数
以上就是Promise对象的一些基本特性,下面我们根据Promise对象的特性,自己来实现一个简单的Promise对象

第一步、Promise对象用三种状态分别是:pending、fulfilled、rejected。
resolve的参数为一个Promise对象的实例的时时候该实例的resolve的参数即为外层Promise对象then方法中onResolved方法的参数,reject的参数即为外层Promise对象then方法(或catch方法)中onRejected方法的参数

function timeout2() { return new Promise((resolve, reject) => { setTimeout(() => { try { a // 此处a未定义,如果注释掉这里即正常执行 resolve(200) } catch (e) { reject(e) } }, 1000) }) } function timeout(time) { return new Promise((resolve, reject) => { setTimeout(() => { try { resolve(timeout2()) } catch (e) { reject(e) } }, time) }) } let p = timeout(1000); p.then(res => { console.log(res); // 200 }).catch(err => { console.log(err); // ReferenceError: a is not defined }) 

由上面的例子我们可以定义一个基本框架:

/*
    Promise构造函数接收一个executor函数, executor函数执行完同步或异步操作后,调用它的两个参数resolve和reject
    如果操作成功,调用resolve并传入value
    如果操作失败,调用reject并传入reason
*/
class MyPromise {
    constructor(executor) { if(typeof executor !== 'function') { throw new Error('MyPromise must accept a function as a parameter') } // Promise当前的状态 this.status = 'pending' // Promise的值 this.data = undefined // Promise resolve时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面 this.onResolvedCallback = [] // Promise reject时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面 this.onRejectedCallback = [] /* 考虑到执行executor的过程中有可能出错,所以我们用try/catch块给包起来, 并且在出错后以catch到的值reject掉这个Promise,另外因为resolve和reject在外部调用故需要绑定this */ try { executor(this.resolve.bind(this), this.reject.bind(this)) } catch (err) { this.reject(err) } } resolve(value) { // 成功时将状态改为fulfilled if(this.status === 'padding') { // 如果传入的值是一个promise实例,则必须等待该Promise对象状态改变后, // 当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态 if(value instanceof MyPromise) { value.then(res => { this.data = res this.status = 'fulfilled' //执行resolve的回调函数,将value传递到callback中 this.onResolvedCallback.forEach(callback => callback(res)) }, err => { this.data = err this.status = 'rejected' //执行reject的回调函数,将reason传递到callback中 this.onRejectedCallback.forEach(callback => callback(err)) }) } else { this.status = 'fulfilled'; this.data = value; //执行resolve的回调函数,将value传递到callback中 this.onResolvedCallback.forEach(callback => callback(value)) } } } reject(reason) { // 失败时将状态改为rejected if(this.status === 'padding') { this.status = 'rejected' this.data = reason; // 触发所有的回调函数 this.onRejectedCallback.forEach(item => { item(reason) }) } } } 

第二步、实现then方法
Promise实例的then方法返回一个新的Promise实例,并接收两个参数onResolved(fulfilled状态的回调); onRejected(rejected状态的回调,该参数可选);

// then方法接收两个参数,onResolved,onRejected,分别为Promise成功或失败后的回调
class MyPromise {
    // .... then(onResolved, onRejected) { // 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理 onResolved = typeof onResolved === 'function' ? onResolved : value => {} onRejected = typeof onRejected === 'function' ? onRejected : reason => {} if (this.status === 'fulfilled') { return new MyPromise((resolve, reject) => { }) } if (this.status === 'rejected') { return new MyPromise((resolve, reject) => { }) } if (this.status === 'pending') { return new MyPromise((resolve, reject) => { }) } } } 

当我们在链式调用Promise实例的时候,当一个实例的then方法返回另一个实例,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为fulfilled,就调用第一个回调函数(即onResolved),如果状态变为rejected,就调用第二个回调函数(即onRejected)。

let p1 = new Promise((resolve,reject) => { setTimeout(function(){ try { resolve(1) } catch (e) { reject(e) } }, 100) }) let p2 = new Promise((resolve,reject) => { setTimeout(function(){ try { resolve(2) } catch (e) { reject(e) } }, 100) }) p1.then(res => { console.log(res); // 1 return p2 }).then(res => { console.log(res); // 2 }).catch(err => { }) 

第三步、完善then方法

根据上面的例子我们来补充then方法里面的内容

// then方法接收两个参数,onResolved,onRejected,分别为Promise成功或失败后的回调
class MyPromise {
    // .... then(onResolved, onRejected) { // 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理 onResolved = typeof onResolved === 'function' ? onResolved : value => value onRejected = typeof onRejected === 'function' ? onRejected : reason => reason // 如果promise1(此处即为this)的状态已经确定并且是resolved,我们调用onResolved // 因为考虑到有可能throw,所以我们将其包在try/catch块里 if (this.status === 'fulfilled') { return new MyPromise((resolve, reject) => { try { let result = onResolved(this.data) // 如果onResolved的返回值是一个Promise对象,直接取它的结果做为返回promise实例的结果 if(result instanceof MyPromise) { result.then(resolve, reject) } resolve(result) // 否则,以它的返回值做为返回promise实例的结果 } catch (e) { reject(e) } }) } // rejected状态的处理方法与上面基本一致 if (this.status === 'rejected') { return new MyPromise((resolve, reject) => { try { let result = onRejected(this.data) // 如果onRejected的返回值是一个Promise对象,直接取它的结果做为返回promise实例的结果 if(result instanceof MyPromise) { result.then(resolve, reject) } } catch (e) { reject(e) } }) } if (this.status === 'pending') { /** * 如果当前的Promise还处于pending状态,我们并不能确定调用onResolved还是onRejected, * 只能等到Promise的状态确定后,才能确实如何处理。 * 所以我们需要把以上两种情况的处理逻辑做为callback放入promise1(此处即this)的回调数组里 * 具体逻辑也与上面类似 */ return new MyPromise((resolve, reject) => { this.onResolvedCallback.push((value) => { try { let result = onResolved(this.data); if (result instanceof MyPromise) { result.then(resolve, reject) } } catch (e) { reject(e) } }) this.onRejectedCallback.push(reason => { try { let result = onRejected(this.data) if (result instanceof MyPromise) { result.then(resolve, reject) } } catch (e) { reject(e) } }) }) } } } 

第四步、完成catch方法
以上我们就实现了一个promise对象的基本功能,最后加上catch方法完整代码如下


/*
    Promise构造函数接收一个executor函数, executor函数执行完同步或异步操作后,调用它的两个参数resolve和reject
    如果操作成功,调用resolve并传入value
    如果操作失败,调用reject并传入reason
*/
class MyPromise {
    constructor(executor) { if(typeof executor !== 'function') { throw new Error('MyPromise must accept a function as a parameter') } // Promise当前的状态 this.status = 'pending' // Promise的值 this.data = undefined // Promise resolve时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面 this.onResolvedCallback = [] // Promise reject时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面 this.onRejectedCallback = [] /* 考虑到执行executor的过程中有可能出错,所以我们用try/catch块给包起来, 并且在出错后以catch到的值reject掉这个Promise,另外因为resolve和reject在外部调用故需要绑定this */ try { executor(this.resolve.bind(this), this.reject.bind(this)) } catch (err) { this.reject(err) } } resolve(value) { // 成功时将状态改为fulfilled if(this.status === 'padding') { // 如果传入的值是一个promise实例,则必须等待该Promise对象状态改变后, // 当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态 if(value instanceof MyPromise) { value.then(res => { this.data = res this.status = 'fulfilled' //执行resolve的回调函数,将value传递到callback中 this.onResolvedCallback.forEach(callback => callback(res)) }, err => { this.data = err this.status = 'rejected' //执行reject的回调函数,将reason传递到callback中 this.onRejectedCallback.forEach(callback => callback(err)) }) } else { this.status = 'fulfilled'; this.data = value; //执行resolve的回调函数,将value传递到callback中 this.onResolvedCallback.forEach(callback => callback(value)) } } } reject(reason) { // 失败时将状态改为rejected if(this.status === 'padding') { this.status = 'rejected' this.data = reason; // 触发所有的回调函数 this.onRejectedCallback.forEach(item => { item(reason) }) } } then(onResolved, onRejected) { // 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理 onResolved = typeof onResolved === 'function' ? onResolved : value => value onRejected = typeof onRejected === 'function' ? onRejected : reason => reason // 如果promise1(此处即为this)的状态已经确定并且是resolved,我们调用onResolved // 因为考虑到有可能throw,所以我们将其包在try/catch块里 if (this.status === 'fulfilled') { return new MyPromise((resolve, reject) => { try { let result = onResolved(this.data) // 如果onResolved的返回值是一个Promise对象,直接取它的结果做为返回promise实例的结果 if(result instanceof MyPromise) { result.then(resolve, reject) } resolve(result) // 否则,以它的返回值做为返回promise实例的结果 } catch (e) { reject(e) } }) } // rejected状态的处理方法与上面基本一致 if (this.status === 'rejected') { return new MyPromise((resolve, reject) => { try { let result = onRejected(this.data) // 如果onRejected的返回值是一个Promise对象,直接取它的结果做为返回promise实例的结果 if(result instanceof MyPromise) { result.then(resolve, reject) } } catch (e) { reject(e) } }) } if (this.status === 'pending') { /** * 如果当前的Promise还处于pending状态,我们并不能确定调用onResolved还是onRejected, * 只能等到Promise的状态确定后,才能确实如何处理。 * 所以我们需要把以上两种情况的处理逻辑做为callback放入promise1(此处即this)的回调数组里 * 具体逻辑也与上面类似 */ return new MyPromise((resolve, reject) => { this.onResolvedCallback.push((value) => { try { let result = onResolved(this.data); if (result instanceof MyPromise) { result.then(resolve, reject) } } catch (e) { reject(e) } }) this.onRejectedCallback.push(reason => { try { let result = onRejected(this.data) if (result instanceof MyPromise) { result.then(resolve, reject) } } catch (e) { reject(e) } }) }) } } catch(onRejected) { return this.then(null, onRejected) } } 

以上就实现了Promise对象的核心功能,其他的几个特性很容易实现在此不做赘述,由于本人水平有限,如有错漏之处请指出斧正。



作者:XZ阳光小熊
链接:https://www.jianshu.com/p/b9ec2e3c3ad8
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

猜你喜欢

转载自www.cnblogs.com/jokeryf/p/12084154.html