1. 先搭建整体结构
要点:
- 注意函数对象和原型对象上的方法,哪些是函数对象上的方法,哪些是原型对象上的方法
/**
* 自定义Promise函数模块
*
*/
(function (params) {
/**
* Promise构造函数
* excutor:执行器函数,同步执行
*/
function Promise(excutor) {}
/**
* Promise原型对象的then()
* 指定成功和失败的回调函数
* 返回一个新的Promise对象
*/
Promise.prototype.then = function(onResolved, onReject) {
}
/**
* Promise原型对象的catch()
* 指定失败的回调函数
* 返回一个新的Promise
*/
Promise.prototype.catch = function(onReject) {
}
/**
* Promise函数对象的resolve方法
* 返回一个指定结果数据成功的promise,值为value
*/
Promise.resolve = function (value) {}
/**
* Promise函数对象的reject方法
* 返回一个指定结果数据失败的promise,值为reason
*/
Promise.reject = function (reason) {}
/**
* Promise函数对象的all方法
* 返回一个promise,只有当所有都成功时才返回,否则只要有一个失败的就失败
*/
Promise.all = function (promiseArr) {}
/**
* Promise函数对象的race方法
* 返回一个promise,其结果由第一个完成的promise的结果决定
*/
Promise.race = function (promiseArr) {}
// 向外暴露Promise函数
window.Promise = Promise
})(window)
2. 实现构造函数
要点:
- 构造函数的入参是执行器函数,执行器函数有两个参数:resolve 和 reject,执行器函数是立即执行
- 提前声明的变量:status:控制状态的改变,data:存储数据,callbacks,存储回调函数
- 提前声明的函数:改变为成功状态的函数resolve() {} 和 改变为失败状态的函数reject() {}
/**
* Promise构造函数
* excutor:执行器函数,同步执行
*/
function Promise(excutor) {
this.status = 'pending'
this.data = undefined
this.callbacks = [] // 存储格式为{ onResolved, onRejected },也就是外界调用then函数时传递的参数
function resolve(value) {}
function reject(reason) {}
excutor(resolve, reason)
}
2.1 实现将状态改变为成功态的函数resolve()
要点:
- 将状态从 'pending' 改变为 'resolved'
- 将外界传递的value存储下来
- !!!判断状态是否已经变为resolved,是则直接结束,保证外界多次调用resolve()函数时,只有第一次有效
- !!!判断回调队列里是否已经有待执行的回调函数,有则 异步执行,为什么会有这种情况:当还未执行resolve函数时,就已经存储了成功的回调函数,也就是执行了then函数,then函数是同步执行。
- !!!如果回调队列中有待执行的回调函数时,不能直接执行,如果直接执行的话,外界在new Promise时执行resolve时,回调便会执行,将违背回调异步执行的原理,所以需要异步执行回调函数,用setTimeout模拟异步队列
function resolve(value) {
// 如果当前状态不是pending,直接结束
if(this.status != 'pending') return
// 将状态改为resolved
this.status = 'resolved'
// 保存value数据
this.data = value
// 如果有待执行的callback函数,立即异步执行回调
if(this.callbacks.length) {
setTimeout(() => { // 将回调函数放到宏任务的列表,保证异步执行,(模拟队列)
this.callbacks.forEach(callbacksObj => {
callbacksObj.onResolved(value)
});
})
}
}
2.2 实现将状态改变为失败态的函数reject()
- 实现基本与 resolve() 函数类似
function reject(reason) {
// 如果当前状态不是pending,直接结束
if(this.status != 'pending') return
// 将状态改为rejected
this.status = 'rejected'
// 保存value数据
this.data = reason
// 如果有待执行的callback函数,立即异步执行回调
if(this.callbacks.length) {
setTimeout(() => { // 将回调函数放到宏任务的列表,保证异步执行,(模拟队列)
this.callbacks.forEach(callbacksObj => {
callbacksObj.onRejected(reason)
});
})
}
}
2.3 考虑执行器函数执行期间 throw Error 的情况
-
由于excutor函数执行期间,会有报错的情况,此时,状态由 'pending' 变为 'rejected', 所以直接执行 excutor(resolve, reject) 是不当的
-
当catch捕获到错误时,可直接执行reject函数,改变状态
// 立即同步执行excutor
try {
excutor(resolve, reject)
} catch (error) {
reject(error)
}
3. 实现原型对象的then方法
要点:
- then方法的功能是:通过制定成功或失败的回调函数,获取promise更改状态后的值
- 参数:成功回调onResolved函数 和 失败回调 onRejected函数,都是异步执行
- 返回值:返回一个新的promise对象,保证then的链式回调
- 因为返回值是一个promise且保证链式调用,所以onResolved 和 onRejected 执行时有三种情况,分别处理三种对应的情况,传递给下一个then:
-
- 执行回调时抛出异常,状态变为rejected,返回error
-
- 如果回调执行结果返回的不是promise对象,状态变为resolved,返回value
-
- 如果回调执行结果返回的是promise对象,状态由这个promise决定,结果也是由这个promise决定
3.1 考虑入参和出参
Promise.prototype.then = function(onResolved, onRejected) {
return new Promise((resolve, reject) => {
// 通过调用当前的promise的resolve, reject,改变then的状态以及告诉外界返回值
})
}
3.2 考虑三种状态下的处理逻辑(pending、resolved、rejected)
要点:
- pending状态的情况是:当执行器中的resolve或reject异步执行或还未执行,先执行了then方法,此时要把回调函数存储在回调队列中(所以执行器中的resolve和reject要判断回调队列的长度)
- resolved状态是:执行器中的resolve函数同步执行了,才执行then方法,此时要立即执行回调函数
- rejected状态是:执行器中的reject函数同步执行了,才执行then方法,此时要立即执行回调函数
Promise.prototype.then = function(onResolved, onRejected) {
return new Promise((resolve, reject) => {
// 通过调用当前的promise的resolve, reject,改变then的状态以及告诉外界返回值
const _this = this
if (_this.status == PENDING) {
// 当前状态还是pending状态,将回调函数存储起来,then先执行resolve还没执行时,就是pending状态
_this.callbacks.push({
onResolved,
onRejected
})
}
if (_this.status == RESOLVED) {
setTimeout(() => {// 注意回调都是异步执行的
onResolved()
})
}
if (_this.status == REJECTED) {
setTimeout(() => {// 注意回调都是异步执行的
onRejected()
})
}
})
}
此时的问题是回调函数直接执行后,并没有改变返回的promise的状态,也没有返回值
3.3 解决返回的promise的状态变更及返回值问题
要点:
- 也就说要通过new的promise的resolve和reject方法来改变返回的promise对象的状态
- 同时将 value 或者 reason 传递出去
- 考虑上面说的三种情况,也就是说外界调用then方法时可能出现的三种情况:抛出异常、是一个promise对象、不是一个promise对象
先以 RESOLVED 状态下为例说明:
Promise.prototype.then = function(onResolved, onRejected) {
const _this = this
return new Promise((resolve, reject) => {
// 通过调用当前的promise的resolve, reject,改变then的状态以及告诉外界返回值
if (_this.status == RESOLVED) {
setTimeout(() => {// 注意回调都是异步执行的
// 情况一:try catch捕获抛出的异常,并且结果变为rejected
try {
const result = onResolved(_this.data)
if (result instanceof Promise) {
// 情况三:回调中返回的是一个promise对象,通过then方法获取结果到底是成功还是失败
result.then(resolve, reject)
} else {
// 情况二:回调中返回的不是一个promise对象,
// 通过返回的promise的resolve方法,将状态改变为resolved,并且告诉下一级此时是resolved,值是result
resolve(result)
}
} catch (error) {
//通过返回的promise的reject方法,将状态改变为rejected,并且告诉下一级此时是rejected,原因是error
reject(error)
}
})
}
})
}
REJECTED 状态与 RESOLVED 类似:
Promise.prototype.then = function(onResolved, onRejected) {
const _this = this
return new Promise((resolve, reject) => {
// 通过调用当前的promise的resolve, reject,改变then的状态以及告诉外界返回值
if (_this.status == REJECTED) {
setTimeout(() => {// 注意回调都是异步执行的
// 情况一:try catch捕获抛出的异常,并且结果变为rejected
try {
const result = onRejected(_this.data)
if (result instanceof Promise) {
// 情况三:回调中返回的是一个promise对象,通过then方法获取结果到底是成功还是失败
result.then(resolve, reject)
} else {
// 情况二:回调中返回的不是一个promise对象,
// 通过返回的promise的resolve方法,将状态改变为resolved,并且告诉下一级此时是resolved,值是result
resolve(result)
}
} catch (error) {
//通过返回的promise的reject方法,将状态改变为rejected,并且告诉下一级此时是rejected,原因是error
reject(error)
}
})
}
})
}
提取公共处理函数,优化then方法:
Promise.prototype.then = function(onResolved, onRejected) {
const _this = this
return new Promise((resolve, reject) => {
// 通过调用当前的promise的resolve, reject,改变then的状态以及告诉外界返回值
const handle = function (callback) {
try {
const result = callback(_this.data)
if (result instanceof Promise) {
result.then(resolve, reject)
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
}
if (_this.status == RESOLVED) {
setTimeout(() => {// 注意回调都是异步执行的
handle(onResolved)
})
}
if (_this.status == REJECTED) {
setTimeout(() => {// 注意回调都是异步执行的
handle(onRejected)
})
}
})
}
此时还剩 PENDING 状态没有考虑,当前 PENDING 时的逻辑是存储回调函数,那什么时候执行呢? 已经存储到毁掉队列的回调函数,等待resolve或reject执行的时候执行。 所以then中的成功回调和失败回调迟早会执行,只是时机不同 然后只要执行,就需要考虑返回的promise的状态和值
if (_this.status == PENDING) {
// 当前状态还是pending状态,将回调函数存储起来,then先执行resolve还没执行时,就是pending状态
_this.callbacks.push({
onResolved: () => {
handle(onResolved)
},
onRejected: () => {
handle(onRejected)
}
})
}
此时then的逻辑差不多完成了,整合一下就是: 主要逻辑就是封装函数handle处理返回的promise的状态变更以及值,分别处理三种状态下的逻辑
Promise.prototype.then = function(onResolved, onRejected) {
const _this = this
/**
* then函数执行回调函数(onResolved, onRejected)时,有三种情况:
* 1. 如果抛出异常,状态变为rejected,返回error
* 2. 如果不是promise对象,状态变为resolved,返回value
* 3. 如果是promise对象,状态由这个promise决定,结果也是由这个promise决定
*/
/**
* then函数中涉及到执行回调(onResolved, onRejected)的地方都要做handle处理,因为then返回的是一个promise
* promise就要改变对应的状态,使用new的promise的resolve和reject来改变返回的promise对象的状态
*/
return new Promise((resolve, reject) => {
//封装处理执行回调函数时的三种情况,这样返回的promise对象可以正确的改变状态
const handle = function (callback) {
try {
const result = callback(_this.data)
if (result instanceof Promise) {
result.then(resolve, reject)
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
}
if (_this.status == PENDING) {
// 当前状态还是pending状态,将回调函数存储起来,then先执行resolve还没执行时,就是pending状态
_this.callbacks.push({
onResolved: () => {
handle(onResolved)
},
onRejected: () => {
handle(onRejected)
}
})
}
if (_this.status == RESOLVED) {
setTimeout(() => {
handle(onResolved)
})
}
if (_this.status == REJECTED) {
setTimeout(() => {
handle(onRejected)
})
}
})
}
3.4 最后再处理一下参数的异常情况
/**
* 处理参数的异常情况
*/
onResolved = typeof onResolved == 'function' ? onResolved : v => v
onRejected = typeof onRejected == 'function' ? onRejected : reason => { throw reason }
有 reason => { throw reason } 这个才能做到异常穿透
then方法完整版
Promise.prototype.then = function(onResolved, onRejected) {
const _this = this
/**
* 处理参数的异常情况
*/
onResolved = typeof onResolved == 'function' ? onResolved : v => v
onRejected = typeof onRejected == 'function' ? onRejected : reason => { throw reason }
return new Promise((resolve, reject) => {
//封装处理执行回调函数时的三种情况,这样返回的promise对象可以正确的改变状态
const handle = function (callback) {
try {
const result = callback(_this.data)
if (result instanceof Promise) {
result.then(resolve, reject)
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
}
if (_this.status == PENDING) {
// 当前状态还是pending状态,将回调函数存储起来,then先执行resolve还没执行时,就是pending状态
_this.callbacks.push({
onResolved: () => {
handle(onResolved)
},
onRejected: () => {
handle(onRejected)
}
})
}
if (_this.status == RESOLVED) {
setTimeout(() => {
handle(onResolved)
})
}
if (_this.status == REJECTED) {
setTimeout(() => {
handle(onRejected)
})
}
})
}
4. 实现原型对象的catch方法
要点:
- 入参:onRejected回调,指定失败的回调函数
- 出参:返回一个新的Promise
Promise.prototype.catch = function(onReject) {
return this.then(undefined, onReject) // 必须指定成功的回调,哪怕是undefined
}
5. 实现函数对象的resolve方法
要点:
- 入参:value,可以是一个基本类型,也可以是一个promise对象
- 出参:
- 返回一个指定结果数据成功或失败的promise
- 如果是基本类型,状态变为resolved,返回值为value
- 如果是promise对象,则返回promise对象执行的结果
/**
* Promise函数对象的resolve方法
* 返回一个指定结果数据成功的promise,值为value
*/
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(resolve, reject)
} else {
resolve(value)
}
})
}
6. 实现函数对象的reject方法
要点:
- 入参:reason,具体返回的原因,基本类型
- 出参:
- 返回一个指定结果数据为失败的promise
/**
* Promise函数对象的reject方法
* 返回一个指定结果数据失败的promise,值为reason
*/
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
7. 实现函数对象的all方法
要点:
- 功能:主要是等多个异步函数都完成后才返回
- 入参:含多个promise对象的数组
- 出参:返回一个promise,只有当所有都成功时才返回,否则只要有一个失败的就失败
Promise.all = function (promiseArr) {
let resolvedCount = 0
const values = new Array(promiseArr.length)
return new Promise((resolve, reject) => {
promiseArr.forEach((p,index) => {
Promise.resolve(p).then(
value => {
resolvedCount++
values[index] = value
if (resolvedCount == promiseArr.length) {
resolve(values)
}
},
reason => {
// 只要有失败的就执行reject
reject(reason)
}
)
})
})
}
该方法存在的问题是:当只要有一个失败的promise就执行reject,但是循环并没有中断,后面的成功的回调还是会执行,但是resolvedCount 和 promiseArr.length是不会相等的。
8. 实现函数对象的race方法
/**
* Promise函数对象的race方法
* 返回一个promise,其结果由第一个完成的promise的结果决定
*/
Promise.race = function (promiseArr) {
return new Promise((resolve, reject) => {
promiseArr.forEach((p, index) => {
Promise.resolve(p).then(resolve, reject)
})
})
}