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对象的核心功能,其他的几个特性很容易实现在此不做赘述,由于本人水平有限,如有错漏之处请指出斧正。