JS异步编程2:手写Promise
Posted 沿着路走到底
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JS异步编程2:手写Promise相关的知识,希望对你有一定的参考价值。
1
class MyPromise
constructor(fn)
// promise 的状态
this.promiseState = 'pending'
// promise 的值
this.promiseResult = undefined
// 记录then中的callback
this.thenCallBack = null
// 记录catch中的callback
this.catchCallBack = null
if(fn)
fn(this.resolve, this.reject)
static resolve(value)
return new MyPromise(function(resolve, reject)
resolve(value)
)
static reject(errValue)
return new MyPromise(function(resolve, reject)
reject(errValue)
)
static all(promiseArr)
const resArr = []
return new MyPromise(function(resolve, reject)
promiseArr.forEach((item, index) =>
item.then(res =>
resArr[index] = res
const allResolved = promiseArr.every(_item =>
return _item.promiseState === 'fulfilled'
)
if (allResolved)
resolve(resArr)
).catch(err =>
reject(err)
)
)
)
static race(promiseArr)
let end = false
return new MyPromise(function(resolve, reject)
promiseArr.forEach(item =>
item.then(function(res)
resolve(res)
).catch(function(err)
if (end === false)
end = true
reject(err)
)
)
)
resolve = (value) =>
if (this.promiseState === 'pending')
this.promiseState = 'fulfilled'
this.promiseResult = value
if (value instanceof MyPromise)
value.then((res) =>
console.log(res)
if (this.thenCallBack)
this.thenCallBack(res)
)
else
setTimeout(() =>
if (this.thenCallBack)
this.thenCallBack(value)
)
reject = (errValue) =>
if (this.promiseState === 'pending')
this.promiseState = 'rejected'
this.promiseResult = errValue
setTimeout(() =>
if (this.catchCallBack)
this.catchCallBack(errValue)
else if (this.thenCallBack)
this.thenCallBack(errValue)
else
throw('catch is not defined')
)
then = (callback) =>
return new MyPromise((resolve, reject) =>
this.thenCallBack = function(value)
if (this.promiseState === 'rejected')
reject(value)
else
const res = callback && callback(value)
if (res instanceof MyPromise && res.promiseState === 'rejected')
res.catch(function(errValue)
reject(errValue)
)
else
resolve(res)
)
catch = (callback) =>
return new MyPromise((resolve, reject) =>
this.catchCallBack = function(errValue)
const res = callback && callback(errValue)
resolve(res)
)
Promise
对象的基本结构定义
根据
Promise
对象的特点分析,
Promise
存在状态属性和
Promise
的值的属性。初始化
Promise
时需要传⼊⼀个回调函数来进⾏对象的基本设置,回调函数具备两个参数resolve
和
reject
,两个参数均为函数。所以初始化代码如下
:
function MyPromise(fn)
//promise的初始状态为pending,可变成fulfilled或rejected其中之⼀
this.promiseState = 'pending'
this.promiseValue = undefined
var resolve = function()
var reject = function()
if(fn)
fn(resolve,reject)
else
throw('Init Error,Please use a function to init MyPromise!')
根据对象特性,初始化
Promise
时的回调函数是同步执⾏的,所以此时的
fn
直接调⽤即可。
在调⽤
resolve
和
reject
时,需要将
Promise
对象的状态设置为对应的
fulfilled
和
rejected
,其中需要传⼊
Promise
当前的结果,所以此时应该将resolve
和
reject
修改为如下结构。
//保存上下⽂对象
var _this = this
var resolve = function(value)
if(_this.promiseState == 'pending')
_this.promiseState = 'fulfilled'
_this.promiseValue = value
var reject = function(value)
if(_this.promiseState == 'pending')
_this.promiseState = 'rejected'
_this.promiseValue = value
定义完内部结构之后需要思考
Promise
在状态变更为
fulfilled
以及状态变更为
rejected
时对应的
then
和
catch
会相应 执⾏,所以需要将对象的两个函数初始化:
MyPromise.prototype.then = function(callback)
MyPromise.prototype.catch = function(callback)
那么初始对象的结构应该整体是这样的:
function MyPromise(fn)
//promise的初始状态为pending,可变成fulfilled或rejected其中之⼀
this.promiseState = 'pending'
this.promiseValue = undefined
var _this = this
var resolve = function(value)
if(_this.promiseState == 'pending')
_this.promiseState = 'fulfilled'
_this.promiseValue = value
var reject = function(value)
if(_this.promiseState == 'pending')
_this.promiseState = 'rejected'
_this.promiseValue = value
if(fn)
fn(resolve,reject)
else
throw('Init Error,Please use a function to init MyPromise!')
MyPromise.prototype.then = function(callback)
MyPromise.prototype.catch = function(callback)
实现
then
的调⽤
在实现了初始结构之后,我们需要使⽤
MyPromise
按照
Promise
的⽅式进⾏编程,来实现他的流程控制部分了。⾸先我们需要让then
跑起来。
定义调⽤代码:
var p = new MyPromise(function(resolve,reject)
resolve(123)
)
console.log(p)
p.then(function(res)
console.log(res)
)
此时执⾏代码时控制台会输出如下内容:
MyPromise
promiseState: "fulfilled"
promiseValue: 123
[[Prototype]]: Object
我们发现我们定义的
Promise
对象状态已经变更但是
then
中的回调函数没有执⾏。
接下来我们实现
then
的触发:
//在MyPromise中改造该部分代码如下
//定义then的回调函数
this.thenCallback = undefined
var resolve = function(value)
if(_this.promiseState == 'pending')
_this.promiseState = 'fulfilled'
_this.promiseValue = value
//异步的执⾏then函数中注册的回调函数
setTimeout(function()
if(_this.thenCallback)
_ this.thenCallback(value)
)
//在then中编写如下代码
MyPromise.prototype.then = function(callback)
//then第⼀次执⾏时注册回调函数到当前的Promise对象
this.thenCallback = function(value)
callback(value)
在两处改造完成之后访问⽹⻚会发现控制台上可以输出
then
函数中的回调执⾏的结果并且该结果的参数就是 resolve传⼊的值。
MyPromise promiseState: 'fulfilled', promiseValue: 123, thenCallback: undefined
当前代码效果如下:
function MyPromise(fn)
//promise的初始状态为pending,可变成fulfilled或rejected其中之⼀
this.promiseState = 'pending'
this.promiseValue = undefined
var _this = this
//定义then的回调函数
this.thenCallback = undefined
var resolve = function(value)
if(_this.promiseState == 'pending')
_this.promiseState = 'fulfilled'
_this.promiseValue = value
//异步的执⾏then函数中注册的回调函数
setTimeout(function()
if(_this.thenCallback)
_this.thenCallback(value)
)
var reject = function(value)
if(_this.promiseState == 'pending')
_this.promiseState = 'rejected'
_this.promiseValue = value
if(fn)
fn(resolve,reject)
else
throw('Init Error,Please use a function to init MyPromise!')
MyPromise.prototype.then = function(callback)
//then第⼀次执⾏时注册回调函数到当前的Promise对象
this.thenCallback = function(value)
callback(value)
MyPromise.prototype.catch = function(callback)
var p = new MyPromise(function(resolve,reject)
resolve(123)
)
console.log(p)
p.then(function(res)
console.log(res)
)
实现
then
的异步链式调⽤
通过上⾯的编程已经可以实现
then
⾃动触发,但是当前我们如果将代码变成如下效果时只有⼀个
then
能执⾏。⽽且控制台会报错
var p = new MyPromise(function(resolve,reject)
resolve(123)
)
console.log(p)
p.then(function(res)
console.log(res)
).then(function(res)
console.log(res)
).then(function(res)
console.log(res)
)
控制台信息如下:
MyPromise promiseState: 'fulfilled', promiseValue: 123, thenCallback: undefined
Uncaught TypeError: Cannot read properties of undefined (reading
'then')
针对该情况,我们需要对
Promise
的流程控制代码做进⼀步的加强以实现链式调⽤,并且在链式调⽤的过程中将每次的结果顺利的向下传递。
//resolve部分代码实现
var resolve = function(value)
if(_this.promiseState == 'pending')
_this.promiseValue = value
_this.promiseState = 'fulfilled'
//当传⼊的类型是Promise对象时
if(value instanceof MyPromise)
value.then(function(res)
_this.thenCallback(res)
)
else
//当传⼊的数据类型是普通变量时
setTimeout(function()
if(_this.thenCallback)
_this.thenCallback(value)
)
//then函数代码实现
MyPromise.prototype.then = function(callback)
var _this = this
return new MyPromise(function(resolve,reject)
_this.thenCallback = function(value)
var callbackRes = callback(value)
resolve(callbackRes)
)
将
then
代码修改为如下之后我们将调⽤代码更改如下
var p = new MyPromise(function(resolve)
resolve(new MyPromise(function(resolve1)
resolve1('aaa')
))
)
p.then(function(res)
console.log(res)
return 123
).then(function(res)
console.log(res)
return new MyPromise(function(resolve)
setTimeout(function()
resolve('Promise')
,2000)
)
).then(function(res)
console.log(res)
)
console.log(p)
会惊喜的发现
MyPromise
对象可以正常的⼯作了并且还可以实现何时调⽤
resolve
何时执⾏
then
的操作
MyPromise promiseValue: MyPromise, promiseState: 'fulfilled', catchCallback:
undefined, thenCallback: ƒ
test.html:57 aaa
test.html:60 123
test.html:67 Promise
当前状态的代码如下
function MyPromise(fn)
var _this = this
this.promiseValue = undefined
this.promiseState = 'pending'
this.thenCallback = undefined
this.catchCallback = undefined
var resolve = function(value)
if(_this.promiseState == 'pending')
_this.promiseValue = value
_this.promiseState = 'fulfilled'
if(value instanceof MyPromise)
value.then(function(res)
_this.thenCallback(res)
)
else
setTimeout(function()
if(_this.thenCallback)
_this.thenCallback(value)
)
var reject = function(err)
if(fn)
fn(resolve,reject)
else
throw('Init Error,Please use a function to init MyPromise!')
MyPromise.prototype.then = function(callback)
var _this = this
return new MyPromise(function(resolve,reject)
_this.thenCallback = function(value)
var callbackRes = callback(value)
resolve(callbackRes)
)
var p = new MyPromise(function(resolve)
resolve(new MyPromise(function(resolve1)
resolve1('aaa')
))
)
实现
catch
的流程处理
当
Promise
的对象触发
reject
操作的时候他的状态会变更为
rejected
,此时会触发
catch
函数,并且
catch
函数触发后流程结束。
⾸先仿照
then
的⽅式在
MyPromise
对象中定义好初始通知函数
//定义catch的回调函数
this.catchCallback = undefined
var reject = function(err)
if(_this.promiseState == 'pending')
_this.promiseValue = err
_this.promiseState = 'rejected'
setTimeout(function()
if(_this.catchCallback)
_this.catchCallback(err)
)
然后在
catch
函数中做如下处理
MyPromise.prototype.catch = function(callback)
var _this = this
return new MyPromise(function(resolve,reject)
_this.catchCallback = function(errValue)
var callbackRes = callback(errValue)
resolve(callbackRes)
)
调⽤代码如下
var p = new MyPromise(function(resolve,reject)
reject('err')
)
p.catch(function(err)
console.log(err)
)
当运⾏此时代码时我们会发现我们的
Promise
对象在控制台上可以直接触发
catch
的回调执⾏并输出对应的结果
MyPromise promiseValue: 'err', promiseState: 'rejected', thenCallback: undefined,
catchCallback: ƒ
test.html:73 err
实现跨对象执⾏
catch
在上⾯的案例中已经可以执⾏
MyPromise
的
catch
函数了,但是如果将调⽤代码改为如下⾏为会发现
catch
函数不会执⾏
var p = new MyPromise(function(resolve,reject)
reject(123)
)
console.log(p)
p.then(function(res)
console.log(res)
).catch(function(err)
console.log(err)
)
这是因为按照我们编写的代码流程
Promise
对象会⾃动变更状态为
rejected
并且
catch
的回调函数⽆法注册,所以 Promise的流程就断了。这个时候需要追加判断代码让
Promise
在
rejected
时如果没有
catchCallback
再去检测是否存在thenCallback
var reject = function(err)
if(_this.promiseState == 'pending')
_this.promiseValue = err
_this.promiseState = 'rejected'
setTimeout(function()
if(_this.catchCallback)
_this.catchCallback(err)
else if(_this.thenCallback)
_this.thenCallback(err)
else
throw('this Promise was reject,but can not found catch!')
)
该步骤操作完毕之后我们需要将
then
函数中的逻辑再次更改为如下
MyPromise.prototype.then = function(callback)
var _this = this
//实现链式调⽤并且每个节点的状态是未知的所以每次都需要返回⼀个新的Proimse对象
return new MyPromise(function(resolve,reject)
//then第⼀次执⾏时注册回调函数到当前的Promise对象
_this.thenCallback = function(value)
//判断如果进⼊该回调时Promise的状态为rejected那么就直接触发后续Promise的catchCallback
//直到找到catch
if(_this.promiseState == 'rejected')
reject(value)
else
var callbackRes = callback(value)
resolve(callbackRes)
)
修改如下之后调⽤代码改造为
var p = new MyPromise(function(resolve,reject)
reject('err')
)
p.then(function(res)
console.log(res)
return 111
).then(function(res)
console.log(res)
return 111
).then(function(res)
console.log(res)
return 111
).catch(function(err)
console.log(err)
)
console.log(p)
输出结果为
MyPromise promiseValue: 'err', promiseState: 'rejected', catchCallback: undefined,
thenCallback: ƒ
test.html:91 err
实现链式调⽤的中断
本⽂仅介绍通过返回
Promise
对象来中断链式调⽤,⾸先在
Promise
的原型对象上增加
reject
⽅法如下
:
MyPromise.reject = function(value)
return new MyPromise(function(resolve,reject)
reject(value)
)
然后初始化如下调⽤代码
var p = new MyPromise(function(resolve,reject)
resolve(123)
)
console.log(p)
p.then(function(res)
console.log('then1执⾏')
return 456
).then(function(res)
console.log('then2执⾏')
return MyPromise.reject('中断了')
).then(function(res)
console.log('then3执⾏')
return 789
).then(function(res)
console.log('then4执⾏')
return 666
).catch(function(err)
console.log('catch执⾏')
console.log(err)
)
最后修改调试代码中的
then
MyPromise.prototype.then = function(callback)
var _this = this
return new MyPromise(function(resolve,reject)
_this.thenCallback = function(value)
if(_this.promiseState == 'rejected')
reject(value)
else
var callbackRes = callback(value)
if(callbackRes instanceof MyPromise)
if(callbackRes.promiseState == 'rejected')
callbackRes.catch(function(errValue)
reject(errValue)
)
else
resolve(callbackRes)
)
根据代码分析处理逻辑,然后查看运⾏结果:
MyPromise promiseState: 'fulfilled', promiseValue: 123, thenCallback: undefined,
catchCallback: undefined
promise.html:100 then1执⾏
promise.html:103 then2执⾏
promise.html:112 catch执⾏
promise.html:113 中断了
最后我们发现在返回
Promise.reject()
之后
then
的链式调⽤便中断了。
1
以上是关于JS异步编程2:手写Promise的主要内容,如果未能解决你的问题,请参考以下文章