前言
ES6里的Promise是个原理性蛮复杂但是使用起来很简单的异步解决方案。我们有必要系统、深入的学习Promise的使用规则,并能根据规则手动实现一个完整的Promise,下面我们就根据官方的规则一步一步自定义Promise。
我们边写代码,边测试。Promise.js为源码,index.html为测试代码。
一、初始化结构搭建
1.1 index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<title>我是Promise</title>
<script src="./Promise.js"></script>
</head>
<body>
<script>
let p = new Promise((resolve, reject) => {
resolve('guoyu')
})
console.log(p)
p.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
</script>
</body>
</html>
1.2 Promise.js文件
function Promise(executor) {
}
Promise.prototype.then = function(onResolved, onRejected) {
}
二、构造函数中resolve与reject
在new Promise的时候,构造函数的参数executor是个函数,而且在Promise规则中是同步调用的。
function Promise(executor) {
// 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
function resolve(data) {
}
// 声明reject函数
function reject(data) {
}
// 同步调用「执行器函数」
// 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
// 还要对 resolve 和 reject 进行声明
executor(resolve, reject)
}
Promise.prototype.then = function(onResolved, onRejected) {
}
2.1 resolve/reject函数的实现
在Promise规则中,resolve/reject有两个作用:
- 改变对象状态 PromiseState
- 设置对象结果值 PromiseResult
我们可以在构造函数中设置两个属性:PromiseState、PromiseResult,并赋予初始值,PromiseState初始值是 ‘pending’,因为状态值只可能有两种变化 - pending --> fulfilled
- pending --> rejected
this.PromiseState = 'pending'
this.PromiseResult = null
function Promise(executor) {
this.PromiseState = 'pending'
this.PromiseResult = null
// 保存实例对象 this 的值,这里的this指向以后对象
const self = this
// 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
function resolve(data) {
// 这里的this指向全局window
// 1. 修改对象状态 PromiseState
self.PromiseState = 'fulfilled'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
}
// 声明reject函数
function reject(data) {
// 1. 修改对象状态 PromiseState
self.PromiseState = 'rejected'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
}
// 同步调用「执行器函数」
// 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
// 还要对 resolve 和 reject 进行声明
executor(resolve, reject)
}
Promise.prototype.then = function(onResolved, onRejected) {
}
三、抛出异常改变状态
改变状态有三种情况
- resolve:pending —> fulfilled
- reject: pending —> rejected
- throw XXX 抛出异常: pendg —> rejected
前面讲了1和2,现在对3抛出异常进行处理
<!DOCTYPE html>
<html lang="en">
<head>
<title>我是Promise</title>
<script src="./Promise.js"></script>
</head>
<body>
<script>
let p = new Promise((resolve, reject) => {
// resolve('ok')
// reject('error')
// 抛出异常
throw 'throwError'
})
console.log(p)
</script>
</body>
</html>
Promise.js中没有处理,所以直接报错了
如何处理呢?
try … catch 才能处理throw的异常,那么try…catch往哪里加,throw是在executor函数中抛出的。
try {
executor(resolve, reject)
} catch (err) {
// err 就是使用的时候throw抛出的错误
console.log(err)
// 修改 promise对象状态为失败
reject(err)
}
四、状态只能修改一次
- resolve:pending —> fulfilled
- reject: pending —> rejected
- throw XXX 抛出异常: pendg —> rejected
要确保状态只能修改一次,看看我们上面的代码,假如我同时调用resolve() 和 reject(),你会发现,resolve和reject两个函数都执行了
<script>
let p = new Promise((resolve, reject) => {
resolve('ok')
reject('error')
})
console.log(p)
</script>
看到吗,resolve和reject只能执行一个的,最后resolve和reject都执行了,这肯定不符合Promise规则,所以代码要修改。
加上判断,如果PromiseState不是pending,那就说明已经被修改了,就不能再修改了
// 判断状态
if (self.PromiseState !== 'pending') {
return
}
function Promise(executor) {
this.PromiseState = 'pending'
this.PromiseResult = null
// 保存实例对象 this 的值,这里的this指向以后对象
const self = this
// 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
function resolve(data) {
// 这里的this指向全局window
// 判断状态 ------------------- 重点
if (self.PromiseState !== 'pending') {
return
}
// 1. 修改对象状态 PromiseState
self.PromiseState = 'fulfilled'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
}
// 声明reject函数
function reject(data) {
// 判断状态 ------------------- 重点
if (self.PromiseState !== 'pending') {
return
}
// 1. 修改对象状态 PromiseState
self.PromiseState = 'rejected'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
}
// 同步调用「执行器函数」
// 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
// 还要对 resolve 和 reject 进行声明
try {
executor(resolve, reject)
} catch (err) {
// err 就是使用的时候throw抛出的错误
console.log(err)
// 修改 promise对象状态为失败
reject(err)
}
}
Promise.prototype.then = function(onResolved, onRejected) {
}
五、then方法执行回调
就是实现then方法,在原型链的then方法中调用 onResolved 或者 onRejected方法。什么时候调用呢?根据self.PromseState进行调用。
Promise.prototype.then = function(onResolved, onRejected) {
// 调用回调函数 PromiseState
if (this.PromiseState === 'fulfilled') {
onResolved(this.PromiseResult) // 调用时 p.then(value=>{value是参数},reason=>{})
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult) // 调用时 p.then(value=>{},reason=>{reason是参数})
}
}
如果执行resolve(‘ok’)
<script>
let p = new Promise((resolve, reject) => {
resolve('ok')
// reject('Error1')
// throw 'Error2'
})
console.log(p)
p.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
</script>
如果执行 reject(‘Error1’)
如果执行 throw ‘Error2’
function Promise(executor) {
this.PromiseState = 'pending'
this.PromiseResult = null
// 保存实例对象 this 的值,这里的this指向以后对象
const self = this
// 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
function resolve(data) {
// 这里的this指向全局window
// 判断状态
if (self.PromiseState !== 'pending') {
return
}
// 1. 修改对象状态 PromiseState
self.PromiseState = 'fulfilled'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
}
// 声明reject函数
function reject(data) {
// 判断状态
if (self.PromiseState !== 'pending') {
return
}
// 1. 修改对象状态 PromiseState
self.PromiseState = 'rejected'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
}
// 同步调用「执行器函数」
// 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
// 还要对 resolve 和 reject 进行声明
try {
executor(resolve, reject)
} catch (err) {
// err 就是使用的时候throw抛出的错误
console.log(err)
// 修改 promise对象状态为失败
reject(err)
}
}
Promise.prototype.then = function(onResolved, onRejected) {
// 调用回调函数 PromiseState
if (this.PromiseState === 'fulfilled') {
onResolved(this.PromiseResult) // 调用时 p.then(value=>{value是参数},reason=>{})
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult) // 调用时 p.then(value=>{},reason=>{reason是参数})
}
}
六、异步任务then方法实现
前面讲的是简单的同步实现的情况,在构造函数executor里同步执行 resolve/reject/throw对状态进行修改,然后then函数里根据修改后的状态进行对应调用。
那么问题来了,如果构造函数里的executor里面是异步呢,事实上大部分都是异步的情况,也就是不直接在executor里直接修改Promise的状态,而是异步比如设置定时器,这种情况呢?
<script>
let p = new Promise((resolve, reject) => {
setTimeout(() => {
// 重点关注
resolve('ok')
}, 1000)
})
p.then(value => {
console.log(value) // 没有执行
}, reason => {
console.log(reason)
})
</script>
简单分析就知道代码走到then的时候,定时器里的函数还没执行,p的状态还未改变,还是pending,在prototype.then方法里,没有对pending状态的分析判断,所以加个if分支
Promise.prototype.then = function(onResolved, onRejected) {
// 调用回调函数 PromiseState
if (this.PromiseState === 'fulfilled') {
onResolved(this.PromiseResult)
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult)
}
if (this.PromiseState === 'pending') {
// ... 重点
}
}
继续分析,我们在使用Promise的时候,then里面的回调函数应该是在promise状态改变完成之后再执行,但是状态改变是异步(定时器里)的,所以,要等异步执行得到修改后的状态之后再执行后续回调,所以真正调用 成功/失败 的回调应该是在 Promise.js中 resolve/reject 函数中调用,而不是在 then 函数中调用,虽然不能在then函数中调用,但then函数中可以帮助 记录回调函数。
在then函数中将成功/失败的回调保存起来,等Promise的状态发生变化后在调用,也就是在resolve/reject里调用。
现在再通过异步方式,就能得到正确结果了。
总结:上面两节,其中第五节是同步执行,第六节是异步执行。同步执行很简单如下图所示:
如果是异步的情况,也就是在使用Promise的时候,resolve/reject/throw 的时候是异步的,那么then里的回调函数在源码实现的时候就要保存记录需要执行的回调函数,实际上真正执行的位置是放在构造函数里面的resolve函数和reject函数:
七、指定多个回调
如果使用的时候,一个Promise对象有多个then回调,Promise规则是多个回调都要执行,如下所示:
既然多个回调都要执行,那么上一节写的回调函数就不只是一个对象了,而应该是一个数组,数组里面有多个对象。否则,第N+1个then回调就会把第N个then回调覆盖了。我们应该把所有的then回调都保存下来,具体执行的时候呢,就应该在resolve/reject函数实现中对callbacks进行循环调用。
this.callbacks = []
...
if (this.PromiseState === 'pending') {
this.callbacks.push({
onResolved,
onRejected
})
}
function Promise(executor) {
this.PromiseState = 'pending'
this.PromiseResult = null
// 声明属性,用于保存成功/失败的回调函数,异步情况
this.callbacks = []
// 保存实例对象 this 的值,这里的this指向以后对象
const self = this
// 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
function resolve(data) {
// 判断状态
if (self.PromiseState !== 'pending') {
return
}
// 1. 修改对象状态 PromiseState
self.PromiseState = 'fulfilled'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
// 调用成功的回调函数 ??? 怎么才能调用到呢?
self.callbacks.forEach(item => {
item.onResolved(data)
})
}
// 声明reject函数
function reject(data) {
// 判断状态
if (self.PromiseState !== 'pending') {
return
}
// 1. 修改对象状态 PromiseState
self.PromiseState = 'rejected'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
// 执行回调
self.callbacks.forEach(item => {
item.onRejected(data)
})
}
// 同步调用「执行器函数」
// 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
// 还要对 resolve 和 reject 进行声明
try {
executor(resolve, reject)
} catch (err) {
// err 就是使用的时候throw抛出的错误
// 修改 promise对象状态为失败
reject(err)
}
}
Promise.prototype.then = function(onResolved, onRejected) {
// 调用回调函数 PromiseState
if (this.PromiseState === 'fulfilled') {
onResolved(this.PromiseResult)
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult)
}
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callbacks.push({
onResolved,
onRejected
})
}
}
测试环节:
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok')
// reject('err')
}, 1000)
})
let p = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve('ok')
reject('err')
}, 1000)
})
八、同步任务 then 返回结果
先看一下then的使用
<script>
let p = new Promise((resolve, reject) => {
resolve('ok')
})
const result = p.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
console.log(p)
</script>
then返回结果的特点:
then函数返回的结果是由then指定的回调函数的执行结果决定的。
const result = p.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
1,假如说then里的回调函数返回一个 非promise 的结果,比如 数字,字符串啊之类的,那么result就是一个成功的promise。
2,假如then里的回调函数返回的是一个promise对象,那么你返回的这个promise就决定了then的返回结果。
<script>
let p = new Promise((resolve, reject) => {
resolve('ok')
})
const result = p.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
console.log(result)
</script>
看看运行结果(运行的是官方的Promise),返回一个promise,PromiseResult是undefined,那是因为p.then(…),then里的回调函数没有返回语句,其实就是return undefined;假如说then里的回调函数返回一个 非promise 的结果,比如 数字,字符串,undefined啊之类的,那么result就是一个成功的promise。记着这句话。
那么怎么实现then的返回呢?
首先,then返回的是一个Promise,所以实现肯定是return new Promise(…)。
p.then(…)里面的回调
1、返回非Promise对象
2、返回Promise对象,又分为resolve和reject
3、throw 异常
我们要对这三种情况分别进行源码实现处理
看实现之后的源码:
<!DOCTYPE html>
<html lang="en">
<head>
<title>我是Promise</title>
<script src="./Promise.js"></script>
</head>
<body>
<script>
let p = new Promise((resolve, reject) => {
resolve('ok')
})
const result = p.then(value => {
// 下面是几种测试代码,用于测试几种不同的情况
return 'hello'
// return new Promise((resolve, reject) => {
// // resolve('success')
// reject('No')
// })
// throw 'failed'
}, reason => {
console.log(reason)
})
console.log(result)
</script>
</body>
</html>
function Promise(executor) {
this.PromiseState = 'pending'
this.PromiseResult = null
// 声明属性,用于保存成功/失败的回调函数,异步情况
this.callbacks = []
// 保存实例对象 this 的值,这里的this指向以后对象
const self = this
// 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
function resolve(data) {
// 判断状态
if (self.PromiseState !== 'pending') {
return
}
// 1. 修改对象状态 PromiseState
self.PromiseState = 'fulfilled'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
// 调用成功的回调函数 ??? 怎么才能调用到呢?
self.callbacks.forEach(item => {
item.onResolved(data)
})
}
// 声明reject函数
function reject(data) {
// 判断状态
if (self.PromiseState !== 'pending') {
return
}
// 1. 修改对象状态 PromiseState
self.PromiseState = 'rejected'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
// 执行回调
self.callbacks.forEach(item => {
item.onRejected(data)
})
}
// 同步调用「执行器函数」
// 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
// 还要对 resolve 和 reject 进行声明
try {
executor(resolve, reject)
} catch (err) {
// err 就是使用的时候throw抛出的错误
// 修改 promise对象状态为失败
reject(err)
}
}
Promise.prototype.then = function(onResolved, onRejected) {
return new Promise((resolve, reject) => {
// 调用回调函数 PromiseState
if (this.PromiseState === 'fulfilled') {
let result = onResolved(this.PromiseResult)
// 判断
if (result instanceof Promise) {
result.then(v => {
resolve(v)}, r => {
reject(v)})
} else {
resolve(result)
}
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult)
}
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callbacks.push({
onResolved,
onRejected
})
}
})
}
值得一提的是,如果不是return,而是throw一个异常,那么我们该怎么处理呢?
显然是用try…catch处理
如下图用throw来测试一下代码
测试结果:
九、异步任务then返回结果
上面一节讲了同步的情况,现在看看异步情况。
let p = new Promise((resolve, reject) => {
// 模拟异步情况
setTimeout(() => {
resolve('ok')
}, 1000)
})
我们先用官方的Promise
再用自己实现的Promise
测试结果如下:
诶?奇怪了,为啥明明我一秒后resolve(‘ok’)了,但结果却是pending和null。
<!DOCTYPE html>
<html lang="en">
<head>
<title>我是Promise</title>
<script src="./Promise.js"></script>
</head>
<body>
<script>
let p = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve('ok')
reject('no')
}, 1000)
})
const result = p.then(value => {
return '123'
// return new Promise(...)
// throw 'error'
}, reason => {
// return '123'
// return new Promise(...)
throw 'error'
})
console.log(result)
</script>
</body>
</html>
function Promise(executor) {
this.PromiseState = 'pending'
this.PromiseResult = null
// 声明属性,用于保存成功/失败的回调函数,异步情况
this.callbacks = []
// 保存实例对象 this 的值,这里的this指向以后对象
const self = this
// 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
function resolve(data) {
// 判断状态
if (self.PromiseState !== 'pending') {
return
}
// 1. 修改对象状态 PromiseState
self.PromiseState = 'fulfilled'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
// 调用成功的回调函数 ??? 怎么才能调用到呢?
self.callbacks.forEach(item => {
item.onResolved(data)
})
}
// 声明reject函数
function reject(data) {
// 判断状态
if (self.PromiseState !== 'pending') {
return
}
// 1. 修改对象状态 PromiseState
self.PromiseState = 'rejected'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
// 执行回调
self.callbacks.forEach(item => {
item.onRejected(data)
})
}
// 同步调用「执行器函数」
// 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
// 还要对 resolve 和 reject 进行声明
try {
executor(resolve, reject)
} catch (err) {
// err 就是使用的时候throw抛出的错误
// 修改 promise对象状态为失败
reject(err)
}
}
Promise.prototype.then = function(onResolved, onRejected) {
const self = this
return new Promise((resolve, reject) => {
// 调用回调函数 PromiseState
if (this.PromiseState === 'fulfilled') {
try {
let result = onResolved(this.PromiseResult)
// 判断
if (result instanceof Promise) {
result.then(v => {
resolve(v)}, r => {
reject(v)})
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult)
}
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callbacks.push({
onResolved: function() {
try {
let result = onResolved(self.PromiseResult)
if (result instanceof Promise) {
result.then(v => {
resolve(v)}, r => {
reject(v)})
} else {
resolve(result)
}
} catch (err) {
reject(err)
}
},
onRejected: function() {
try {
let result = onRejected(self.PromiseResult)
if (result instanceof Promise) {
result.then(v => {
resolve(v)}, r => {
reject(v)})
} else {
resolve(result)
}
} catch (err) {
reject(err)
}
}
})
}
})
}
十、上述代码的优化
上面有很多重复代码,需要优化,封装。
先看看上面的代码,then方法的实现中,当PromiseState为同步rejected的时候,还没做多情况处理,套路都是一样,还是分为 promise,非promise,throw几种情况
if (this.PromiseState === 'rejected') {
try {
let result = onRejected(this.PromiseResult)
if (result instanceof Promise) {
result.then(v=>{
resolve(v)}, r=>{
reject(r)})
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
}
修改后的代码:
function Promise(executor) {
this.PromiseState = 'pending'
this.PromiseResult = null
// 声明属性,用于保存成功/失败的回调函数,异步情况
this.callbacks = []
// 保存实例对象 this 的值,这里的this指向以后对象
const self = this
// 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
function resolve(data) {
// 判断状态
if (self.PromiseState !== 'pending') {
return
}
// 1. 修改对象状态 PromiseState
self.PromiseState = 'fulfilled'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
// 调用成功的回调函数 ??? 怎么才能调用到呢?
self.callbacks.forEach(item => {
item.onResolved(data)
})
}
// 声明reject函数
function reject(data) {
// 判断状态
if (self.PromiseState !== 'pending') {
return
}
// 1. 修改对象状态 PromiseState
self.PromiseState = 'rejected'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
// 执行回调
self.callbacks.forEach(item => {
item.onRejected(data)
})
}
// 同步调用「执行器函数」
// 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
// 还要对 resolve 和 reject 进行声明
try {
executor(resolve, reject)
} catch (err) {
// err 就是使用的时候throw抛出的错误
// 修改 promise对象状态为失败
reject(err)
}
}
Promise.prototype.then = function(onResolved, onRejected) {
const self = this
return new Promise((resolve, reject) => {
function callback(type) {
try {
let result = type(self.PromiseResult)
// 判断
if (result instanceof Promise) {
result.then(v => {
resolve(v)}, r => {
reject(r)})
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
}
// 调用回调函数 PromiseState
if (this.PromiseState === 'fulfilled') {
callback(onResolved)
}
if (this.PromiseState === 'rejected') {
callback(onRejected)
}
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callbacks.push({
onResolved: function() {
callback(onResolved)
},
onRejected: function() {
callback(onRejected)
}
})
}
})
}
十一、catch方法与异常穿透
Promise.prototype.catch = function(onRejected) {
return this.then(undefined, onRejected)
}
那么如何穿透呢?所谓穿透,就是最后一个catch,前面的错误或者异常无论隔多少层个then,都能穿透到最后那个catch中
<script>
let p = new Promise((resolve, reject) => {
setTimeout(() => {
reject('no')
}, 1000)
})
const result = p.then(value => {
console.log(111)
}).then(value => {
console.log(222)
}).then(value => {
console.log(333)
}).catch(r => {
console.log(r)
})
console.log(result)
</script>
我们看看原生Promise运行结果:
尽管隔了好几层then,可还是被catch捕捉了。
那我们自己写的呢?
会报一个错误,onRejected is not a function。为什么呢?
而且官方的Promise中then()函数是两个参数都不传也可以,而我们实现的Promise默认认为两个参数都传了,在代码中直接就用了,所以一旦遇到不传的时候就报错了,算是一个容错问题。
十二、Promise.resolve函数实现
我们在使用Promise的时候会用到Promise.resolve,可见resolve并不是原型链上的函数,属于Promise函数对象本身的一个方法。
Promise.resolve(传值) 返回的是一个Promise,它的状态由传入的值来决定。如果传入的是一个非Promise类型数据,那么返回就是一个成功的Promise,而且传入的参数就是返回的Promise的成功的结果值;如果你传入的是一个Promise类型的值,那么返回的状态和值就是你传入的Promise的状态和结果来决定。
Promise.resolve = function(value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(v => {
resolve(v)}, r => {
reject(r)})
} else {
resolve(value)
}
})
}
十三、Promise.reject函数的实现
Promise.reject()无论你里面传任何参数,返回的永远都是一个失败的Promise,传什么都不好使。看看官方内置的Promise的运行结果
<script>
let p = Promise.reject('error')
let p2 = Promise.reject(new Promise((resolve, reject) => {
resolve('OK')
}))
console.log(p)
console.log(p2)
</script>
我们来实现自己的Promise.reject()函数,因为无论如何都是返回失败的Promise,所以实现起来比resolve方便,不用判断了
十四、Promise.all()方法的封装
看看下面的代码实现,错在哪里?
Promise.all = function(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
resolve(v)
}, r => {
reject(r)
})
}
})
}
其实应该是n个promises都成功才能执行resolve函数,而不是有一个成功就resolve了。
function Promise(executor) {
this.PromiseState = 'pending'
this.PromiseResult = null
// 声明属性,用于保存成功/失败的回调函数,异步情况
this.callbacks = []
// 保存实例对象 this 的值,这里的this指向以后对象
const self = this
// 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
function resolve(data) {
// 判断状态
if (self.PromiseState !== 'pending') {
return
}
// 1. 修改对象状态 PromiseState
self.PromiseState = 'fulfilled'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
// 调用成功的回调函数 ??? 怎么才能调用到呢?
self.callbacks.forEach(item => {
item.onResolved(data)
})
}
// 声明reject函数
function reject(data) {
// 判断状态
if (self.PromiseState !== 'pending') {
return
}
// 1. 修改对象状态 PromiseState
self.PromiseState = 'rejected'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
// 执行回调
self.callbacks.forEach(item => {
item.onRejected(data)
})
}
// 同步调用「执行器函数」
// 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
// 还要对 resolve 和 reject 进行声明
try {
executor(resolve, reject)
} catch (err) {
// err 就是使用的时候throw抛出的错误
// 修改 promise对象状态为失败
reject(err)
}
}
Promise.prototype.then = function(onResolved, onRejected) {
const self = this
if (typeof onRejected !== 'function') {
// 如果不传递onRejected,我们给个默认处理,抛出异常
// 这个容错处理,不仅解决了undefined问题,更是解决了穿透问题
// 相当于是给每个then提供了一个默认的onRejected函数
onRejected = reason => {
throw reason
}
}
// 同理,如果使用的时候没传onResolved,我们也可以指定一个默认的处理方式
if (typeof onResolved !== 'function') {
onResolved = value => value
}
return new Promise((resolve, reject) => {
function callback(type) {
try {
let result = type(self.PromiseResult)
// 判断
if (result instanceof Promise) {
result.then(v => {
resolve(v)}, r => {
reject(r)})
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
}
// 调用回调函数 PromiseState
if (this.PromiseState === 'fulfilled') {
callback(onResolved)
}
if (this.PromiseState === 'rejected') {
callback(onRejected)
}
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callbacks.push({
onResolved: function() {
callback(onResolved)
},
onRejected: function() {
callback(onRejected)
}
})
}
})
}
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(v => {
resolve(v)}, r => {
reject(r)})
} else {
resolve(value)
}
})
}
Promise.reject = function(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
Promise.all = function(promises) {
return new Promise((resolve, reject) => {
let count = 0
let arr = []
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
count++
// 将成功的promise放在数组里,注意是按顺序放,i要对准
// 不要arr.push(v),因为各个promise返回的顺序不一定是原来的顺序
arr[i] = v
if (count === promises.length) {
resolve(v)
}
}, r => {
reject(r)
})
}
})
}
十五、Promise.race方法
race简单,几个promise,哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。实现代码中循环执行,就算后面的promise返回了,虽然能执行,但是已经无法修改promise的状态了,因为前面的已经修改了。
Promise.race = function(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
resolve(v)
}, r => {
reject(r)
})
}
})
}
十六、then方法回调的异步执行
此节略。
十七、改装成class版本
class Promise {
constructor(executor) {
this.PromiseState = 'pending'
this.PromiseResult = null
// 声明属性,用于保存成功/失败的回调函数,异步情况
this.callbacks = []
// 保存实例对象 this 的值,这里的this指向以后对象
const self = this
// 声明resolve函数,使用resolve函数的时候是传入参数了,所以还要有形参
function resolve(data) {
// 判断状态
if (self.PromiseState !== 'pending') {
return
}
// 1. 修改对象状态 PromiseState
self.PromiseState = 'fulfilled'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
// 调用成功的回调函数 ??? 怎么才能调用到呢?
self.callbacks.forEach(item => {
item.onResolved(data)
})
}
// 声明reject函数
function reject(data) {
// 判断状态
if (self.PromiseState !== 'pending') {
return
}
// 1. 修改对象状态 PromiseState
self.PromiseState = 'rejected'
// 2. 设置对象结果值 PromiseResult
self.PromiseResult = data
// 执行回调
self.callbacks.forEach(item => {
item.onRejected(data)
})
}
// 同步调用「执行器函数」
// 需要在执行器函数中传入两个参数,而且两个参数又都还是函数
// 还要对 resolve 和 reject 进行声明
try {
executor(resolve, reject)
} catch (err) {
// err 就是使用的时候throw抛出的错误
// 修改 promise对象状态为失败
reject(err)
}
}
then(onResolved, onRejected) {
const self = this
if (typeof onRejected !== 'function') {
// 如果不传递onRejected,我们给个默认处理,抛出异常
// 这个容错处理,不仅解决了undefined问题,更是解决了穿透问题
// 相当于是给每个then提供了一个默认的onRejected函数
onRejected = reason => {
throw reason
}
}
// 同理,如果使用的时候没传onResolved,我们也可以指定一个默认的处理方式
if (typeof onResolved !== 'function') {
onResolved = value => value
}
return new Promise((resolve, reject) => {
function callback(type) {
try {
let result = type(self.PromiseResult)
// 判断
if (result instanceof Promise) {
result.then(v => {
resolve(v)}, r => {
reject(r)})
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
}
// 调用回调函数 PromiseState
if (this.PromiseState === 'fulfilled') {
callback(onResolved)
}
if (this.PromiseState === 'rejected') {
callback(onRejected)
}
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callbacks.push({
onResolved: function() {
callback(onResolved)
},
onRejected: function() {
callback(onRejected)
}
})
}
})
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
resolve = function(value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(v => {
resolve(v)}, r => {
reject(r)})
} else {
resolve(value)
}
})
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
static all(promises) {
return new Promise((resolve, reject) => {
let count = 0
let arr = []
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
count++
// 将成功的promise放在数组里,注意是按顺序放,i要对准
// 不要arr.push(v),因为各个promise返回的顺序不一定是原来的顺序
arr[i] = v
if (count === promises.length) {
resolve(v)
}
}, r => {
reject(r)
})
}
})
}
static race(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
resolve(v)
}, r => {
reject(r)
})
}
})
}
}