模块化封装
由于是希望此Promise
是用来应用到页面上的,所以还是使用ES5
中实现模块的方式来写,即:使用立即执行函数来模拟模块化。然后将Promise
构造函数放到window
的属性上
// Promise.js文件
(function (obj) {
// 注意:此处obj是形参,为了和实参window区别,所以故意不用window
function Promise() {
}
// 将Promise放到传递进来的对象上,作为对象的方法
obj.Promise = Promise
})(window)
声明构造函数
// Promise.js文件
(function (obj) {
/*
声明构造函数
并接收执行器函数excutor作为参数
*/
function Promise(excutor) {
}
obj.Promise = Promise
})(window)
添加所有方法
- 有些方法在原型上,用来给实例对象使用的
- 有些方法直接在构造函数上,算是语法糖,可以直接通过构造函数调用。
- 还有各方法的形参,关于这些形参想必熟悉
Promise
的小伙伴应该都懂得他们的含义
// Promise.js文件
(function (obj) {
function Promise(excutor) {
}
Promise.prototype.then = function (onResovled, onRejected) {
}
Promise.prototype.catch = function (onRejected) {
}
Promise.resolve = function (value) {
}
Promise.reject = function (reason) {
}
Promise.all = function (promises) {
}
Promise.race = function (promises) {
}
obj.Promise = Promise
})(window)
实现构造函数
需要结合怎样使用Promise,来考虑构造函数如何实现
// userPromise.js文件
new Promise((resolve, reject) => {
/*
resolve和reject都是由Promise内部提供的,这里只是形参,
起占位作用,以备在执行器函数内部调用
*/
console.log('执行器内部代码!同步的');
})
console.log('Promise构造函数后面代码!'); // 可以通过添加此代码证明执行器内部代码是否是同步执行的
// Promise.js文件
function Promise(excutor) {
// 定义resolve函数,并接成功的结果
function resolve(value) {
}
// 定义reject函数,并接被拒绝的原因
function reject(reason) {
}
// 立即同步执行执行器函数,并传递实参函resolve和reject
excutor(resolve, reject)
}
给Promise实例对象添加三个属性
// 在立即执行函数这一层先声明以下三个常量
(function (obj) {
// 声明状态常量
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
...
})(window)
// Promise.js文件
function Promise(excutor) {
// 用来保存Promise状态的属性,初始值为pending,可以修改为resolved或rejected
this.status = PENDING
// 用来保存成功或被拒绝后结果的属性
this.data = undefined
// 用来保存多个回调函数对象(成功和被拒绝)的数组
// 每个对象的结构:{ onResoled() {}, onRejected() {} }
this.callbacks = []
}
书写resolve函数和reject函数
// Promise.js文件
// 定义resolve函数,并接成功的结果
function resolve(value) {
// 状态只能改一次,非peding状态,直接返回
if (this.status !== PENDING) return
/*
此函数被调用后需要做三件事情
1、修改Promise的状态为resolved
2、保存传递进来的结果到data中
3、如果使用方是先设置回调函数后修改状态的话,
也就意味着可以直接遍历调用callbacks里面的回调函数了
也就是then指定的那些回调函数,是异步执行的。
后面会在then方法中向callbacks中push回调函数
*/
this.status = RESOLVED
this.data = value
setTimeout(() => {
this.callbacks.forEach(obj => {
obj.onResovled(this.data)
})
})
}
// 定义reject函数,并接被拒绝的原因
function reject(reason) {
// 状态只能改一次,非peding状态,直接返回
if (this.status !== PENDING) return
// 同理
this.status = REJECTED
this.data = reason
setTimeout(() => {
this.callbacks.forEach(obj => {
obj.onRejected(this.data)
})
})
}
捕获执行器内部代码抛出的异常
要考虑执行异步任务的时候抛出异常的情况
// usePromise.js文件
const p = new Promise((resolve, reject) => {
// 也就是这里使用的时候可能抛出异常
})
// Promise.js文件
// 要考虑捕获执行器抛出异常
try {
excutor(resolve, reject)
} catch (error) {
reject(error) // 抛出异常的情况下自然需要调用被拒绝的函数reject
}
书写then方法
当状态为
pending
状态的情况下,即使已经指定了回调函数,还不能调用,需要等状态的变化,这个时候就需要先把回调函数保存起来了。正好跟上面resolve
函数对应起来,当先指定了回调函数,后改变状态的时候。就需要先把回调函数保存起来了。
// Promise.js文件
Promise.prototype.then = function (onResovled, onRejected) {
// 保存回调函数
switch(this.status) {
case PENDING:
this.callbacks.push({
onResovled,
onRejected
})
break
}
}
测试刚才的代码
注意这里的条件是先指定回调函数,后修改状态,所以在执行器中需要通过定时器做一下延时调用相应的函数
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功的结果!')
}, 1000)
})
p.then(
value => {
console.log('onResolved1: ' + value);
},
reason => {
console.log('onRejected1: ' + reason);
}
)
p.then(
value => {
console.log('onResolved2: ' + value);
},
reason => {
console.log('onRejected2: ' + reason);
}
)
// 打印结果:
onResolved1: 成功的结果!
onResolved2: 成功的结果!
解决this指向问题
// 将resolve方法和reject方法修改成箭头函数,解决this指向问题
function resolve(value) { ... }
// 修改为
const resolve = value => { ... }
function reject(reason) { ... }
// 修改为
const reject = reason => { ... }
被拒绝结果的测试
const p = new Promise((resolve, reject) => {
setTimeout(() => {
reject('被拒绝的结果!')
}, 1000)
})
// 打印结果:
onRejected1: 被拒绝的结果!
onRejected2: 被拒绝的结果!
测试then的回调函数是否为异步
console.log('then方法后面的代码!');
// 打印结果:
then方法后面的代码!
onResolved1: 成功的结果!
onResolved2: 成功的结果!
打印的顺序说明then的回调函数是异步执行的
继续完善then方法——考虑状态不为pending的情况
当状态为resolved
或rejected
的时候异步调用onResovled
函数或onRejected
函数
Promise.prototype.then = function (onResovled, onRejected) {
// 保存回调函数
switch(this.status) {
case PENDING:
this.callbacks.push({
onResovled,
onRejected
})
break
// 状态为resolved的时候异步调用onResovled函数
case RESOLVED:
setTimeout(() => {
onResovled(this.data)
})
break
// 状态为rejected的时候异步调用onRejected函数
case REJECTED:
setTimeout(() => {
onRejected(this.data)
})
break
}
}
继续完善then方法——要返回一个新的promise实例对象
- 首先需要用一个实例化的
Promise
构造函数把then
方法里面的所有代码包含在内,并且返回此promise
实例对象 - 返回的新的
promise
实例对象的状态和结果取决于promise
中执行器excutor
的执行结果或者前面的then
中的回调函数onResovled
或onRejected
执行的结果所以需要考虑以下三种情况。以下为了书写方便把以上两种情况统称为前面
前面
抛出异常:新promise
状态就该为rejected
,结果为error
前面
返回非promise
类型的值:新promise
状态就该为resovled
,结果为此值前面
返回promise
类型的值:新promise
状态和结果就该为前面
promise
的状态和结果
问:为什么上面要提到 promise
中执行器excutor
和 then
中的回调函数onResovled
或onRejected
两种情况?
答:因为我们不仅要考虑第一次实例化Promise
构造函数后调用then
方法,而且还要考虑then
的链式调用。
基于以上思路,添加以下代码
Promise.prototype.then = function (onResovled, onRejected) {
// 返回一个新的promise实例对象
return new Promise(resolve, reject) {
switch(this.status) {
...
case RESOLVED:
setTimeout(() => {
try { // a、正常情况
// 将结果保存起来判断是否为promise类型的结果
const result = onResovled(this.data)
if (result instanceof Promise) {
// 3、前面返回promise类型的值:新promise状态和结果就该为前面promise的状态和结果
// 要获取前面的promise的结果就需要调用前面promise的then方法
/* result.then(
// 在前面promise的then方法里面的成功的回调函数里面
// 调用新的promise的resolve方法,并传递值
value => {
resolve(value)
},
// 在前面promise的then方法里面的被拒绝的回调函数里面
// 调用新的promise的reject方法,并传递值
reason => {
reject(reason)
}
) */
// 以上代码的简化版
result.then(resolve, reject)
} else {
// 2、前面返回非promise类型的值:新promise状态就该为resovled,结果为此值
resolve(result)
}
} catch (error) { // b、异常情况
// 1、前面抛出异常:新promise状态就该为rejected,结果为error
reject(error)
}
})
break
// 状态为rejected的时候异步调用onRejected函数
case REJECTED:
setTimeout(() => {
// 以下代码跟上面同理
try {
const result = onRejected(this.data) // 只修改了这里
if (result instanceof Promise) {
result.then(resolve, reject)
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
})
break
}
}
}
提取出根据实例化结果的不同情况怎样进一步处理的函数handle
return new Promise((resolve, reject) => {
const handle = callback => {
try {
const result = callback(this.data)
if (result instanceof Promise) {
result.then(resolve, reject)
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
}
switch(this.status) {
...
case RESOLVED:
setTimeout(() => {
handle(onResovled)
})
break
case REJECTED:
setTimeout(() => {
handle(onRejected)
})
break
}
})
考虑pending状态
- 对保存起来的回调函数在上面的resolve和reject中遍历的调用后
- 再调用handle函数做进一步的处理,主要还是要改变promsie的状态和传递结果
- 且上面已经异步调用了,所以这里就不用异步了,直接同步调用即可
switch(this.status) {
case PENDING:
this.callbacks.push({
onResovled() {
handle(onResovled)
},
onRejected() {
handle(onRejected)
}
})
break
...
}
上面之所以把
onResovled
和onRejected
又包装了一层handle
,就是因为不仅要调用这两个方法,而且还要修改promise
的状态和传递结果。所以不仅要保存两个回调函数,而且还需要调用,而且看调用后的结果等等。
考虑异常传透
要考虑在没有传递第二个回调函数onRejected
的时候,处理异常传透,主要是需要把原因抛出去,传递给后面捕获的地方
Promise.prototype.then = function (onResovled, onRejected) {
onRejected = typeof onRejected === 'function'
? onRejected
: reason => { throw reason }
// 随便把onResovled也处理一下,如果传递的不是函数的话,
// 把结果不做任何处理而是直接往下继续传递
onResovled = typeof onResovled === 'function'
? onResovled
: value => value
...
}
实现catch方法
Promise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected)
}
测试刚才的代码
// usePromise.js文件
const p = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve('成功的结果!')
reject('被拒绝的结果!')
}, 1000)
}).then(
value => {
console.log('onResolved1: ' + value);
// return 1
// throw 'no'
// return new Promise((resolve, reject) => reject(1))
},
reason => {
console.log('onRejected1: ' + reason);
// return 1
// throw 'no'
return new Promise((resolve, reject) => reject(1))
}
).then(
value => {
console.log('onResolved2: ' + value);
},
reason => {
console.log('onRejected2: ' + reason);
}
).catch(
reason => {
console.log('onRejected3: ' + reason);
}
)
实现resolve方法
- 根据传递参数的不同,promise的状态也会不同
- 传递非
promise
类型值 - 传递调用
resolve
的promsie
类型值 - 传递调用
reject
的promsie
类型值
- 传递非
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
// 根据value的值不同,修改状态
if (value instanceof Promise) {
value.then(resolve, reject) // promise类型值
} else {
resolve(result) // 非promise类型值
}
})
}
测试实验原生Promise的结果
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(Promise.resolve(2))
const p3 = Promise.resolve(Promise.reject(3))
p1.then(value => {console.log('p1: ' + value);})
p2.then(value => {console.log('p2: ' + value);})
p3.catch(reason => {console.log('p3: ' + reason);})
// 打印结果:
p1: 1
p2: 2
p3: 3
实现reject方法
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
实现all方法
Promise.all = function (promises) {
// 声明用来保存成功的多个promise的数组,数组长度就按照传递的数组的长度设置
const resolvedPromises = new Array(promises.length)
// 用来统计成功的promise的数量的变量
let resolvedCount = 0
return new Promise((resolve, reject) => {
promises.forEach((p, index) => {
// p.then(
// 考虑到传递进来的数组的每个元素不一定都是promise,所以需要包一层promise
Promise.resolve(p).then(
value => {
resolvedCount++
// 不能用push,因为push追加的顺序是按照成功先后的顺序
// resolvedCount.push(value)
// 应该按照传递进来的数组的顺序来放置
resolvedPromises[index] = value
// 只有全部都成功了,结果的promise的状态才能改为成功
if (resolvedCount === promises.length) {
resolve(resolvedPromises)
}
},
reason => { // 只要有一个被拒绝了,结果就是被拒绝的状态
reject(reason)
}
)
})
})
}
测试all方法
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(Promise.resolve(2))
const p3 = Promise.resolve(Promise.reject(3))
const p4 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(4)
})
})
const pAll = Promise.all([6, p4, p1, p2])
pAll.then(
values => {
console.log('onResolved: ', values);
},
reason => {
console.log('onRejected: ', reason);
}
)
实现race方法
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
promises.forEach((p) => {
// p.then(
// 考虑到传递进来的数组的每个元素不一定都是promise,所以需要包一层promise
Promise.resolve(p).then(
value => { // 只要有成功就立即改变状态为成功
resolve(value)
},
reason => { // 只要有被拒绝就立即改变状态为被拒绝
reject(reason)
}
)
})
})
}
测试race方法
const p1 = Promise.resolve(Promise.resolve(1))
const p2 = Promise.resolve(Promise.resolve(2))
const p3 = Promise.reject(3)
const p4 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(4)
})
})
const p5 = Promise.resolve(5)
const pRace = Promise.race([6, p5, p4, p3, p2, p1])
pRace.then(
value => {
console.log('onResolved: ', value);
},
reason => {
console.log('onRejected: ', reason);
}
)
增加延时处理的两个方法
实现resolveDelay
Promise.resolveDelay = function (value, time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (value instanceof Promise) {
value.then(resolve, reject)
} else {
resolve(value)
}
}, time)
})
}
实现rejectDelay
Promise.rejectDelay = function (reason, time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(reason)
}, time)
})
}
测试代码
const p1 = Promise.resolveDelay(1, 2000)
const p2 = Promise.rejectDelay(2, 3000)
p1.then(value => console.log('p1:' + value))
p2.catch(reason => console.log('p2:' + reason))
最终简洁版
(function (obj) {
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function Promise(excutor) {
this.status = PENDING
this.data = undefined
this.callbacks = []
const resolve = value => {
if (this.status !== PENDING) return
this.status = RESOLVED
this.data = value
setTimeout(() => {
this.callbacks.forEach(obj => {
obj.onResovled(this.data)
})
})
}
const reject = reason => {
if (this.status !== PENDING) return
this.status = REJECTED
this.data = reason
setTimeout(() => {
this.callbacks.forEach(obj => {
obj.onRejected(this.data)
})
})
}
try {
excutor(resolve, reject)
} catch (error) {
reject(error)
}
}
Promise.prototype.then = function (onResovled, onRejected) {
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
onResovled = typeof onResovled === 'function' ? onResovled : value => value
return new Promise((resolve, reject) => {
const handle = callback => {
try {
const result = callback(this.data)
if (result instanceof Promise) {
result.then(resolve, reject)
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
}
switch(this.status) {
case PENDING:
this.callbacks.push({
onResovled() {
handle(onResovled)
},
onRejected() {
handle(onRejected)
}
})
break
case RESOLVED:
setTimeout(() => {
handle(onResovled)
})
break
case REJECTED:
setTimeout(() => {
handle(onRejected)
})
break
}
})
}
Promise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected)
}
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(resolve, reject) // promise类型值
} else {
resolve(value) // 非promise类型值
}
})
}
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
Promise.all = function (promises) {
const resolvedPromises = new Array(promises.length)
let resolvedCount = 0
return new Promise((resolve, reject) => {
promises.forEach((p, index) => {
Promise.resolve(p).then(
value => {
resolvedCount++
resolvedPromises[index] = value
if (resolvedCount === promises.length) {
resolve(resolvedPromises)
}
},
reason => {
reject(reason)
}
)
})
})
}
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
promises.forEach((p) => {
Promise.resolve(p).then(
value => {
resolve(value)
},
reason => {
reject(reason)
}
)
})
})
}
Promise.resolveDelay = function (value, time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (value instanceof Promise) {
value.then(resolve, reject)
} else {
resolve(value)
}
}, time)
})
}
Promise.rejectDelay = function (reason, time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(reason)
}, time)
})
}
obj.Promise = Promise
})(window)