小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
本文同时参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金
Promise 核心逻辑实现
本文的目标,通过一点点的拆解了解Promise的原理,从而达到我们手写Promise的目的
需求整理
巧妇难为无米之炊,在我们手撸代码之前,我们需要先整理一下Promise
如何使用,Promise
的一些特性,方便我们更好的进行代码的编写,所以我们要先知道啥是Promise
!
1.调用Promise
调用Promise
,我们就需要创建一个Promise
对象,首先我们要通过 new
关键字进行创建
new Promise()
从这段代码中我们可以发现Promise
是一个类
2.入参
调用Promise
我们需要传入一个回调函数,我们称之为执行器 ,这个执行器会立刻执行,也就是说当我们在执行new Promise(fn)
的时候,函数中的fn
会被立刻调用
new Promise(() => {
})
3.执行器参数
当我们传入一个回调函数的时候,会获得两个参数resolve
与reject
,这两个参数实际上是两个函数,调用这两个函数会改变Promise
的状态。
new Promise((resolve,reject) => {
})
4.Promise
的状态
Promise
中有三种状态,分别是:
- 成功 fulfilled
- 失败 rejected
- 等待 pending
5.Promise
的状态改变特点
Promise
的状态变更只能是以下两种情况:
- 等待转变为成功(
pending => fulfilled
) - 等待转变为失败(
pending => rejected
) 【注意:】 一旦状态确定就不可更改
6. 如何修改Promise
的状态?传递成功或失败的值?
可以通过resolve
与reject
进行状态的修改
resolve:
new Promise((resolve,reject) => {
resolve('成功')
})
reject:
new Promise((resolve,reject) => {
reject(失败)
})
7.then方法
这里给一个链接: Promise.prototype.then() 看看这个链接,然后我们整理一下需求
then()
方法返回一个 Promise (en-US)
。它最多需要有两个参数:Promise
的成功和失败情况的回调函数。
那也就是说then
方法内部做的事情就是判断状态,根据状态的不同调用不同的函数,每个Promise
都可以调用到then
方法,也就是说这个方法是原型链上的方法
let promise = new Promise((resolve,reject) => {
reject(失败)
})
promise.then(()=>{}, ()=>{})
8.then的回调参数
我们先看看以下代码
let promise = new Promise((resolve,reject) => {
reject(失败)
})
// promise.then(onFulfilled[, onRejected]);
promise.then(value => { // fulfillment }, reason => { // rejection});
结合我之前发的那个文档链接我们可以得出以下结论: then的成功和失败回调,都有一个参数表示成功/失败的值
需求整理
整理一下我们的需求:
Promise
是一个类,在执行这个类的时候,我们需要传入一个回调函数,且回调函数会立刻执行Promise
中的回调函数会获得两个参数resolve
与reject
,这两个参数实际上是两个函数Promise
中有三种状态分别为:- 成功 fulfilled
- 失败 rejected
- 等待 pending
Promise
只能通过resolve
与reject
进行状态的修改,且通过这两个函数进行数据的传递Promise
的状态变更一旦完成之后,就不能修改且Promise
的状态变更只能有两种情况:- 等待转变为成功(
pending => fulfilled
) - 等待转变为失败(
pending => rejected
)
- 等待转变为成功(
Promise
的then
方法根据状态的不同调用不同的函数且个方法是原型链上的方法Promise
的then
方法成功和失败回调,都有一个参数表示成功/失败的值
开始编写
1. Promise
是一个类
简简单单的第一步,Promise
的一小步,我这个彩笔程序员的的一大步
class MyPromise {
}
2.需要传入一个回调函数
这个我们使用类中的constructor
进行接收,constructor
是一种用于创建和初始化class创建的对象的特殊方法。
class MyPromise {
constructor(callback) {
}
}
3.回调函数会立刻执行
class MyPromise {
constructor(callback) {
callback()
}
}
4. Promise
中的回调函数有两个为函数的参数resolve
与reject
这里我们就需要为 callback
中传入resolve
与reject
,且在MyPromise
定义resolve
与reject
函数,这里我们定义为箭头函数
class MyPromise {
...
resolve = () => {
}
reject = () => {
}
}
Q:
A: 我们回忆一下我们该怎么调用这两个函数
new Promise((resolve,reject) => {
resolve('成功')
})
或
new Promise((resolve,reject) => {
reject(失败)
})
是不是都是这样直接调用的?如果这个函数是一个普通函数的话,那他的this
指向是不是就是window
或者undefined?
我们实验一下
普通函数:
箭头函数:
这里我们来复习一个知识点:
普通函数的this指向是在调用的时候确定的,而箭头函数的this指向是在定义的时候确定的!
5.Promise
中有三种状态
我们需要定义状态的常量,还有用来改变的状态值变量并且让他默认等于等待(pending
)
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
...
}
Q: 定义常量的好处是啥?
A: 咳咳!主要是为了代码的复用(颤音)
6.Promise
通过resolve
与reject
进行状态的修改,且通过这两个函数进行数据的传递
这里我们就需要对resolve
与reject
进行修改
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
// Promise 状态
status = PENDING
// 成功的值
value = undefined
// 失败的值
reason = undefined
constructor(callback) {
callback(this.resolve,this.reject)
}
resolve = value => {
// 将状态更改为成功
this.status = FULFILLED
// 保存成功的值
this.value = value
}
reject = value => {
// 将状态更改为失败
this.status = REJECTED
// 保存失败的值
this.reason = value
}
}
7.Promise
的状态变更只能有两种情况(完成之后不能进行修改)
这里我们主要还是对resolve
与reject
进行修改,我们需要进行判断status
当前的状态如何,是否为等待,如果是进行修改状态,如果不是就不能进行修改
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
...
resolve = () => {
// 如果状态不是等待,阻止程序向下执行
if(this.status !== PENDING){
return
}
// 将状态更改为成功
this.status = FULFILLED
// 保存成功的值
this.value = value
}
reject = () => {
// 如果状态不是等待,阻止程序向下执行
if(this.status !== PENDING){
return
}
// 将状态更改为失败
this.status = REJECTED
// 保存失败的值
this.reason = value
}
}
8.Promise
的then
方法根据状态的不同调用不同的函数且个方法是原型链上的方法
那我们需要在类中定义then
方法,且进行判断,如果状态是成功的就调用成功的回调,失败就调用失败的回调
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
...
then( successCallback, failCallback) {
// 如果是成功的状态
if(this.status === FULFILLED){
successCallback()
}else if(this.status === REJECTED){
failCallback()
}
}
}
9.Promise
的then
方法成功和失败回调,都有一个参数表示成功/失败的值
我们需要在then的时候,将成功/失败的值传递回去
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
...
then( successCallback, failCallback) {
// 如果是成功的状态
if(this.status === FULFILLED){
successCallback(this.value)
}else if(this.status === REJECTED){
failCallback(this.reason)
}
}
}
到这里我们的核心模块就实现完毕了 测试一下
Promise 核心逻辑实现总结
敲黑板 敲黑板 敲黑板
Promise
是一个类,在执行这个类的时候,我们需要传入一个回调函数,且回调函数会立刻执行Promise
中的回调函数会获得两个参数resolve
与reject
,这两个参数实际上是两个函数Promise
中有三种状态分别为:- 成功 fulfilled
- 失败 rejected
- 等待 pending
Promise
只能通过resolve
与reject
进行状态的修改,且通过这两个值进行状态保存Promise
的状态变更一旦完成之后,就不能修改且Promise
的状态变更只能有两种情况:- 等待转变为成功(
pending => fulfilled
) - 等待转变为失败(
pending => rejected
)
- 等待转变为成功(
Promise
的then
方法根据状态的不同调用不同的函数且个方法是原型链上的方法Promise
的then
方法成功和失败回调,都有一个参数表示成功/失败的值
完整代码
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
// Promise 状态
status = PENDING
// 成功的值
value = undefined
// 失败的值
reason = undefined
constructor(callback) {
callback(this.resolve,this.reject)
}
resolve = value => {
// 如果状态不是等待,阻止程序向下执行
if(this.status !== PENDING){
return
}
// 将状态更改为成功
this.status = FULFILLED
// 保存成功的值
this.value = value
}
reject = value => {
// 如果状态不是等待,阻止程序向下执行
if(this.status !== PENDING){
return
}
// 将状态更改为失败
this.status = REJECTED
// 保存失败的值
this.reason = value
}
then( successCallback, failCallback) {
// 如果是成功的状态
if(this.status === FULFILLED){
successCallback(this.value)
}else if(this.status === REJECTED){
failCallback(this.reason)
}
}
}
Promise
加入异步逻辑
众所周知Promise
中,我们主要解决了异步的调用的一些问题,但是,就我们目前的代码而言,使用异步还是有一些问题的
首先根据我们上一篇整理的需求:
Promise
是一个类,在执行这个类的时候,我们需要传入一个回调函数,且回调函数会立刻执行
然后看看下面这个执行
let promise1 = new MyPromise((resolve,reject) => {
setTimeOut(() =>{
resolve(1)
},2000)
})
我们的回调函数会立刻执行,但是改变状态的resolve
方法却包含在异步任务中,导致我们的then
模块的调用懵逼了,状态对不上啊!
then( successCallback, failCallback) {
// 如果是成功的状态
if(this.status === FULFILLED){
successCallback(this.value)
}else if(this.status === REJECTED){
failCallback(this.reason)
}
}
then
模块此时的心情
所以我们要进行一下对
then
方法的改造
改造then方法
- 我们需要在判断添加 等待的时候的情况
- 解决方法: 添加一个else判断即可
- 等待情况下,我们要临时存储失败/成功的回调
- 解决方法: 添加来个实例属性,存储成功/失败的回调
代码实现
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
...
// 成功回调
successCallback = undefined
// 失败回调
failCallback = undefined
...
then( successCallback, failCallback) {
// 如果是成功的状态
if(this.status === FULFILLED){
successCallback(this.value)
}else if(this.status === REJECTED){
failCallback(this.reason)
}else{
// 等待的情况
this.successCallback = successCallback
this.failCallback = failCallback
}
}
}
但是这这是解决了 then
的时候的存储问题,还没有解决异步函数执行完毕的执行问题啊?
接着往下看!
异步执行完毕的回调
异步状态的时候,我们还是会执行在resolve
与reject
,所以在执行到resolve
与reject
时,我们在resolve
与reject
中调用一下我们保存下来的方法就好
代码实现
resolve = value => {
// 如果状态不是等待,阻止程序向下执行
if(this.status !== PENDING){
return
}
// 将状态更改为成功
this.status = FULFILLED
// 保存成功的值
this.value = value
// 判断成功回调是否存在,如果存在 调用
this.successCallback && this.successCallback(this.value)
}
reject = value => {
// 如果状态不是等待,阻止程序向下执行
if(this.status !== PENDING){
return
}
// 将状态更改为失败
this.status = REJECTED
// 保存失败的值
this.reason = value
// 判断失败回调是否存在,如果存在 调用
this.failCallback && this.failCallback(this.reason)
}
ok这里我们就执行完毕了
测试一下
const MyPromise = require('./myPromise')
let promise1 = new MyPromise((resolve,reject) => {
setTimeout(() =>{
resolve('成功')
})
})
promise1.then(value=>{
console.log('success',value)
},reasons => {
console.log('failure',reasons)
})
欸,没问题
我们异步函数的处理就完毕了,接下来进入下一章节
完整代码
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
// Promise 状态
status = PENDING
// 成功的值
value = undefined
// 失败的值
reason = undefined
// 成功回调
successCallback = undefined
// 失败回调
failCallback = undefined
constructor (callback) {
callback(this.resolve, this.reject)
}
resolve = value => {
// 如果状态不是等待,阻止程序向下执行
if (this.status !== PENDING) {
return
}
// 将状态更改为成功
this.status = FULFILLED
// 保存成功的值
this.value = value
// 判断成功回调是否存在,如果存在 调用
this.successCallback && this.successCallback(this.value)
}
reject = value => {
// 如果状态不是等待,阻止程序向下执行
if (this.status !== PENDING) {
return
}
// 将状态更改为失败
this.status = REJECTED
// 保存失败的值
this.reason = value
// 判断失败回调是否存在,如果存在 调用
this.failCallback && this.failCallback(this.reason)
}
then (successCallback, failCallback) {
// 如果是成功的状态
if (this.status === FULFILLED) {
console.log('test')
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
// 等待的情况
this.successCallback = successCallback
this.failCallback = failCallback
}
}
}
Promise
实现 then 方法多次调用添加多个处理函数
提出问题
现在又有一个新问题来了,类似下方的代码一样,我们异步的情况下可以进行多次的执行promise1
,可以多次输出
代码测试
let promise1 = new Promise((resolve,reject) => {
setTimeout(() =>{
resolve('成功')
},2000)
})
// 第一次
promise1.then(value=>{
console.log('success',value)
},reasons => {
console.log('failure',reasons)
})
// 第二次
promise1.then(value=>{
console.log('success',value)
},reasons => {
console.log('failure',reasons)
})
// 第三次
promise1.then(value=>{
console.log('success',value)
},reasons => {
console.log('failure',reasons)
})
手写类myPromise
测试
我们实现的类做不到!怎么办?
我们遇到什么困难,都不要怕,微笑着面对它,消除恐惧的最好办法就是面对恐惧、坚持才是胜利、加油、奥利给!
找到问题发生处
首先我们定位一下问题,为什么我们的只执行一次呢?在我们细细的查看代码之后,发现了病因所在
then (successCallback, failCallback) {
// 如果是成功的状态
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
// 等待的情况
this.successCallback = successCallback
this.failCallback = failCallback
}
}
为什么出现这样的情况
首先在我们每次执行then
的时候,并没有把多次的都存储起来,就导致了一个问题,在我们多次调用的时候,异步的情况下(堆栈的问题了,不太了解的同学稍微百度一下哦),successCallback
和failCallback
保存的都是最后一次的值,导致了其他的值并没有被执行到,如图:
ps: 同步情况以及没问题的啦,大家可以测试一下
解决问题的方法
定位了病根,那我们的解决就很方便了:
- 改造
then
函数的失败/成功方法的存储 =>可更改为数组进行存储就好了 - 改造
resolve
与reject
对successCallback
和failCallback
的执行方式 => 循环存储的数组然后执行
改造then
函数
successCallback
和failCallback
要更改为数组
class MyPromise {
...
// 成功回调
successCallback = []
// 失败回调
failCallback = []
...
then (successCallback, failCallback) {
// 如果是成功的状态
if (this.status === FULFILLED) {
console.log('test')
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
// 等待的情况
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
}
}
ok,这里以及修改完毕了!
改造 resolve
与reject
函数
resolve:
resolve = value => {
// 如果状态不是等待,阻止程序向下执行
if (this.status !== PENDING) {
return
}
// 将状态更改为成功
this.status = FULFILLED
// 保存成功的值
this.value = value
// 判断成功回调是否存在,如果存在 调用
// this.successCallback && this.successCallback(this.value)
while(this.successCallback.length){
// 调用shift方法然后每次执行的时候,推出方法出数组栈,并且将数据返回回去
this.successCallback.shift()(this.value)
}
}
reject:
reject = value => {
// 如果状态不是等待,阻止程序向下执行
if (this.status !== PENDING) {
return
}
// 将状态更改为失败
this.status = REJECTED
// 保存失败的值
this.reason = value
// 判断失败回调是否存在,如果存在 调用
// this.failCallback && this.failCallback(this.reason)
while(this.failCallback.length){
// 调用shift方法然后每次执行的时候,推出方法出数组栈,并且将数据返回回去
this.failCallback.shift()(this.reason)
}
}
到这里就解决完毕了
测试
既然做完了看到要进行以下测试,不然怎么验证我们是正确的
测试用例
const myPromise = require('./myPromise')
let promise1 = new myPromise((resolve, reject) => {
setTimeout(() =>{
reject('失败')
},1000)
})
promise1.then(value =>{
console.log(value)
},reason => {
console.log(1)
console.log(reason)
})
promise1.then(value =>{
console.log(value)
},reason => {
console.log(2)
console.log(reason)
})
promise1.then(value =>{
console.log(value)
},reason => {
console.log(3)
console.log(reason)
})
测试结果
完美,其他的用例就不进行截图了,太多了,大家有兴趣自己去测试一下哈!
总结
本小结主要是为了解决Promise
下的then
方法可以多次调用,当他的状态变化的时候,内部的方法依次执行,需要区分同步和异步的情况
- 异步的情况,我们需要存储起来方法,然后进行依次调用
- 同步的情况,我们只需要根据状态调用对应的方法就好
完整代码
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'reject' // 失败
class MyPromise {
// Promise 状态
status = PENDING
// 成功的值
value = undefined
// 失败的值
reason = undefined
// 成功回调
successCallback = []
// 失败回调
failCallback = []
constructor (callback) {
callback(this.resolve, this.reject)
}
resolve = value => {
// 如果状态不是等待,阻止程序向下执行
if (this.status !== PENDING) {
return
}
// 将状态更改为成功
this.status = FULFILLED
// 保存成功的值
this.value = value
// 判断成功回调是否存在,如果存在 调用
// this.successCallback && this.successCallback(this.value)
while(this.successCallback.length){
// 调用shift方法然后每次执行的时候,推出方法出数组栈,并且将数据返回回去
this.successCallback.shift()(this.value)
}
}
reject = value => {
// 如果状态不是等待,阻止程序向下执行
if (this.status !== PENDING) {
return
}
// 将状态更改为失败
this.status = REJECTED
// 保存失败的值
this.reason = value
// 判断失败回调是否存在,如果存在 调用
// this.failCallback && this.failCallback(this.reason)
while(this.failCallback.length){
// 调用shift方法然后每次执行的时候,推出方法出数组栈,并且将数据返回回去
this.failCallback.shift()(this.reason)
}
}
then (successCallback, failCallback) {
// 如果是成功的状态
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
// 等待的情况
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
}
}
Promise
实现then
方法的链式调用
需求整理
Promise
下面的then
方法是可以链式调用的,下一个then
方法的返回值其实是上一个then
方法的回调函数的返回值,我们来看几个案例
1.链式调用第一个函数没有返回值:
代码:
let promise1 = new Promise((resolve, reject) => {
resolve('成功')
})
promise1.then(val => {
console.log(val)
}).then(val =>{
console.log(val)
})
结果:
2.链式调用第一个函数有返回值:
代码:
let promise1 = new Promise((resolve, reject) => {
resolve('成功')
})
promise1.then(val => {
console.log(val)
return 100
}).then(val =>{
console.log(val)
})
结果:
所以可以确定:下一个then
方法的返回值其实是上一个then
方法的回调函数的返回值
这样一来我们需要做两件事情:
then
方法的链式调用- 把上一个的返回值传递给下一个
then
方法
实现then
方法的链式调用
首先我们需要明确一点then
是在Promise
下,如果说我们想要实现then
方法的链式调用,那么每个then
方法都应该返回一个Promise
对象,如果then
都返回一个Promise
对象,这样才方便我们链式调用。
第一步
既然我们要返回一个Promise
,那我们就在then
方法里面返回一个Promise
then (successCallback, failCallback) {
let promiseThen = new MyPromise()
// 如果是成功的状态
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
// 等待的情况
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
return promiseThen
}
第二步
既然已经返回了,那我们是不是就需要对判断进行一些修改呢?
我们回顾一下,在我们执行new Promise
的时候,可以传入一个回调函数进去,且这个回调函数是立即执行的!
那么,我有一个不成熟的想法!将我们then
内部的判断传入MyPromise
中去执行!
then (successCallback, failCallback) {
let promiseThen = new MyPromise(() => {
// 如果是成功的状态
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
// 等待的情况
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
})
return promiseThen
}
到这里我们的then方法的链式调用就编写完毕了
then
方法值的传递
首先我们需要保存他执行的返回值,这个就非常简单了,我们只要保存执行的时候的返回值就好
let x = successCallback(this.value)
下一个问题就来,怎么传递数据呢?
别急再让我们回顾一下!
Promise
通过resolve
与reject
进行状态的修改,且通过这两个函数进行数据的传递
那么我们就可以通过调用resolve
将返回值传递给我们返回出去的promise
数据,也就能实现把上一个的返回值传递给下一个then
方法
let promiseThen = new MyPromise((resolve, reject) => {
// 如果是成功的状态
if (this.status === FULFILLED) {
let x = successCallback(this.value)
resolve(x)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
// 等待的情况
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
})
return promiseThen
结束了吗?不还没有
到这里我们的判断似乎就结束了,但是!还没有结束,如果返回的是一个Promise
对象怎么办?这个对象成功或者失败又该怎么办?撸起袖子加油干!
if (this.status === FULFILLED) {
let x = successCallback(this.value)
resolve(x)
}
这里的resolve(x)
就不能这样写了!要做一些修改,添加一些判断,什么判断呢?
- 判断x是否promise对象
- 如果不是 直接调用 reslove 函数
- 如果是Promise对象根据其结果选择调用
resolve
或reject
函数
考虑到之后的代码复用性,我们把这个判断语句提取出来
// 解析是否promise函数
// 不是直接调用reslove
// 是的话根据情况调用 reslove 或者 reject
function resolvePromise(x, resolve, reject){
if(x instanceof MyPromise){
x.then(resolve, reject)
}else{
resolve(x)
}
}
然后对then
中进行一下修改
then (successCallback, failCallback) {
let promiseThen = new MyPromise((resolve, reject) => {
// 如果是成功的状态
if (this.status === FULFILLED) {
let x = successCallback(this.value)
resolvePromise(x, resolve, reject)
}
...
})
return promiseThen
}
这样子我们就处理好了同步的情况,当然异步的情况也是要处理的啦!
思路是一样的
...
// 等待的情况
this.successCallback.push(() => {
try {
let x = successCallback(this.value)
resolvePromise(x, resolve, reject)
} catch (e) {
reject(e)
}
})
this.failCallback.push(() => {
try {
let x = failCallback(this.reason)
resolvePromise(x, resolve, reject)
} catch (e) {
reject(e)
}
})
这里我们加入了catch
防止执行报错,当然之前的执行也需要添加,ok进入测试模式
测试
测试同步链式调用
const myPromise = require('./myPromise')
let promise = new myPromise((resolve, reject) => {
resolve('成功')
})
let promise1 = new myPromise((resolve, reject) => {
resolve('成功1')
})
promise.then(val => {
console.log('第一次',val)
return promise1
}).then(val =>{
console.log('第二次',val)
return promise
}).then(val =>{
console.log('第三次',val)
return promise1
}).then(val =>{
console.log('第四次',val)
})
预计输出:
- 第一次 成功
- 第二次 成功1
- 第三次 成功
- 第四次 成功1
同步链式调用测试结果
异步链式调用测试
const myPromise = require('./myPromise')
let promise = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功 1s 延迟版')
},1000)
})
let promise1 = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功 0.5延迟版')
},500)
})
promise.then(val => {
console.log('第一次',val)
return promise1
}).then(val =>{
console.log('第二次',val)
return promise
}).then(val =>{
console.log('第三次',val)
return promise1
}).then(val =>{
console.log('第四次',val)
})
预计输出:
- 第一次 成功 1s 延迟版
- 第二次 成功 0.5延迟版
- 第三次 成功 1s 延迟版
- 第四次 成功 0.5延迟版
异步链式调用测试结果
都没问题 okok,解决
总结
then
的链式调用主要是因为我们返回了同样的类,让函数可以继续调用then
方法,在我们编写的时候要注意函数的异步和同步的调用,还有注意我们的方法选择哦!
Promise
的 then
方法链式调用识别 Promise 对象自返回
我们在使用then
方法的时候是可以返回一个对象的,但是我们如果返回原来的Promise
就会造成一个事情,Promise
的循环调用,造成系统bug,我们要避免这种情况,演示这种报错
使用系统的promise
对象进行报错模拟
let promise = new Promise((resolve, reject) => {
resolve(100)
})
let promise1 = promise.then((value) =>{
console.log(value)
return promise1
})
结果
ok,到这里我们的需求就来了,我们该怎么处理这种自己调用自己的情况呢?
实现
实际上也非常简单,我们进入then
方法,还记得我们之前的代码吗?有这么一段let x = successCallback(this.value)
对吧,有了这一段就很简单了,我们只需要判断 x
和我们链式调用返回的thenPromise
是否相同即可!本次改造我们选择resolvePromise
进行
resolvePromise
的改造
resolvePromise
新增一个thenPromise
的入参进行判断,然后如果相同的话,我们就抛出错误即可
function resolvePromise (thenPromise,returnValue, resolve, reject) {
if(thenPromise === returnValue){
return reject(new TypeError('Chaining cycle detected for promise #<MyPromise>'))
}
if (returnValue instanceof MyPromise) {
returnValue.then(resolve, reject)
} else {
resolve(returnValue)
}
}
这里机制的同学就知道然后对应改造一下调用方法的地方,添加一下传参就好啦!
但是问题没有想象中那么简单!
我们看这里的代码
let thenPromise = new MyPromise((resolve, reject) => {
...
let x = successCallback(this.value)
resolvePromise(thenPromise, x, resolve, reject)
...
})
这里我们需要在 resolvePromise(thenPromise, x, resolve, reject)
传入thenPromise
,但是thenPromise
的声明是在new MyPromise
完毕之后才有的,这样回取不到值的!该怎么办???其实也简单! 异步调用!!! setTimeout
!!!!是不是一下就茅塞顿开?开始改造吧!
自己就改改之后贴代码合集
测试
测试代码
const myPromise = require('./myPromise')
let promise = new myPromise((resolve, reject) => {
resolve('成功')
})
let promise1 = promise.then(val => {
console.log(val)
return promise1
})
promise1.then(val => {
}, reason => {
console.log(reason.message)
})
测试结果
完美
then
方法参数变为可选参数
let promise3 = new Promise((resolve, reject) => {
resolve('失败')
})
promise3.then().then().then( value => console.log(value))
Promise
是可以这样不断传递参数直到有回调方法的情况
该怎么实现呢? 很简单的,我们可以这样理解,在没有参数的时候,我们就给他传入一个 value => value
的函数不就好了??? 完美利用特性
上代码
// 创建一个变量
const isFunction = (value) => typeof value === "function";
....
...
then(successCallback, failCallback) {
successCallback = isFunction(successCallback)
? successCallback
: (value) => value;
failCallback = isFunction(failCallback)
? failCallback
: (err) => {
throw err;
};
....
}
....
测试一下
测试用例
const myPromise = require('./myPromise')
let promise = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
},2000)
})
let promise1 = new myPromise((resolve, reject) => {
setTimeout(() => {
reject('失败')
},2000)
})
promise.then().then().then( value => console.log(value,1), value => console.log(value,2))
promise1.then().then().then( value => console.log(value,3), value => console.log(value,4))
预计输出:
- 成功 1
- 失败 4
测试结果
完整代码
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "reject"; // 失败
const isFunction = (value) => typeof value === "function";
class MyPromise {
// Promise 状态
status = PENDING;
// 成功的值
value = undefined;
// 失败的值
reason = undefined;
// 成功回调
successCallback = [];
// 失败回调
failCallback = [];
// 执行器中需要捕获错误
constructor(callback) {
try {
callback(this.resolve, this.reject);
} catch (e) {
this.reject(e);
}
}
resolve = (value) => {
// 如果状态不是等待,阻止程序向下执行
if (this.status !== PENDING) {
return;
}
// 将状态更改为成功
this.status = FULFILLED;
// 保存成功的值
this.value = value;
// 判断成功回调是否存在,如果存在 调用
// this.successCallback && this.successCallback(this.value)
while (this.successCallback.length) {
// 调用shift方法然后每次执行的时候,推出方法出数组栈,并且将数据返回回去
this.successCallback.shift()();
}
};
reject = (value) => {
// 如果状态不是等待,阻止程序向下执行
if (this.status !== PENDING) {
return;
}
// 将状态更改为失败
this.status = REJECTED;
// 保存失败的值
this.reason = value;
// 判断失败回调是否存在,如果存在 调用
// this.failCallback && this.failCallback(this.reason)
while (this.failCallback.length) {
// 调用shift方法然后每次执行的时候,推出方法出数组栈,并且将数据返回回去
this.failCallback.shift()();
}
};
then(successCallback, failCallback) {
successCallback = isFunction(successCallback)
? successCallback
: (value) => value;
failCallback = isFunction(failCallback)
? failCallback
: (err) => {
throw err;
};
let thenPromise = new MyPromise((resolve, reject) => {
// 如果是成功的状态
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let x = successCallback(this.value);
// 判断x是否 MyPromise 类
// 是 就直接
resolvePromise(thenPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
// resolve(x)
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = failCallback(this.reason);
resolvePromise(thenPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
} else {
// 等待的情况
this.successCallback.push(() => {
setTimeout(() => {
try {
let x = successCallback(this.value);
resolvePromise(thenPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.failCallback.push(() => {
setTimeout(() => {
try {
let x = failCallback(this.reason);
resolvePromise(thenPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return thenPromise;
}
}
// 解析是否promise函数
// 不是直接调用reslove
// 是的话根据情况调用 reslove 或者 reject
function resolvePromise(thenPromise, value, resolve, reject) {
if (thenPromise === value) {
return reject(
new TypeError("Chaining cycle detected for promise #<MyPromise>")
);
}
if (value instanceof MyPromise) {
value.then(resolve, reject);
} else {
resolve(value);
}
}
module.exports = MyPromise;
Promise
的all
方法
all
是用来解决异步并发问题的,它允许我们按照异步代码调用的顺序得到异步代码执行的结果
例子
function promise () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功1')
}, 2000)
})
}
function promise1 () {
return new Promise((resolve, reject) => {
resolve('成功1')
})
}
这里有两个函数,如果我们同时调用promise
和promise1
那肯定先得到promise1
的代码,之后在获得promise
的值,但是Promise.all
就可以promise
、promise1
获得这样的执行顺序。
这是怎么实现的呢?
我们先来看看参数和返回值等
参数
返回值
- 如果传入的参数是一个空的可迭代对象,则返回一个已完成(already resolved) 状态的
Promise
。 - 如果传入的参数不包含任何
promise
,则返回一个异步完成(asynchronously resolved)Promise
。注意:Google Chrome 58 在这种情况下返回一个已完成(already resolved) 状态的Promise
。 - 其它情况下返回一个处理中(pending) 的
Promise
。这个返回的promise
之后会在所有的promise
都完成或有一个promise
失败时异步地变为完成或失败。 见下方关于“Promise.all 的异步或同步”示例。返回值将会按照参数内的promise
顺序排列,而不是由调用promise
的完成顺序决定。
说明
此方法在集合多个 promise
的返回结果时很有用。
完成(Fulfillment):
如果传入的可迭代对象为空,Promise.all
会同步地返回一个已完成(resolved)状态的promise
。
如果所有传入的 promise
都变为完成状态,或者传入的可迭代对象内没有 promise
,Promise.all
返回的 promise
异步地变为完成。
在任何情况下,Promise.all
返回的 promise
的完成状态的结果都是一个数组,它包含所有的传入迭代参数对象的值(也包括非 promise
值)。
失败/拒绝(Rejection):
如果传入的 promise
中有一个失败(rejected),Promise.all
异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise
是否完成。
示例
var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([p1, p2, p3]).then(values => {
console.log(values); // [3, 1337, "foo"]
});
实现
看上面的使用示例,Promise.all
, 所以all
方法一定是一个静态方法
ok,开整
1. 添加静态方法all
在myPromise
中添加静态方法,并为他添加形参(类型为数组)
static all (array) {
}
2.返回值
我们回顾一下刚刚看的Promise.all
的返回值,是不是都是一个Promise
对象!!所以!
static all (array) {
return new MyPromise((resolve, reject) =>{
})
}
3.判断值的类型
返回值
有提到,我们需要根据值的类型进行返回,那我们就需要判断我们传入的数组是什么值,如果是不是Promise
对象,就直接放入结果值的数组中,是Promise
对象的话,执行完毕之后再放入结果值的数组中
static all (array) {
return new MyPromise((resolve, reject) =>{
for(const i in array) {
let current = array[i]
if(current instanceof MyPromise){
// MyPromise对象
}else {
// 非MyPromise对象
}
}
})
}
4.存储结果
上面也说了我们需要根据情况进行数据的存储,那么就要按照对应的顺序进行保存结果,万幸数组可以通过key进行值的存储
static all (array) {
// 存储结果
let result = [];
function addData(key, value) {
// 这里是为了按照执行顺序放入值,保证顺序不会乱!
result[key] = value
}
return new MyPromise((resolve, reject) =>{
// 这里为什么用for循环而不用
for(const i in array) {
let current = array[i]
if(current instanceof MyPromise){
// MyPromise对象
current.then(value => addData(i, value), reason => reject(reason))
}else {
// 非MyPromise对象
addData(i, current)
}
}
resolve(result)
})
}
但是这样还是有问题的!是什么问题呢?
它不支持异步操作欸!卧槽卧槽卧槽!太扎心了
为什么不支持异步操作呢?
别问,问就是不会!开玩笑,for操作是同步,导致异步操作不能及时执行啦,就这么简单
完善代码!
也很简单,我们只需要加一个执行了多少次的变量,然后根据这个变量进行判断,如果他当前的执行次数和我们的入参长度一直,我们就让他执行resolve
方法即可
static all (array) {
// 存储结果
let result = [];
let index = [];
return new MyPromise((resolve, reject) =>{
function addData(key, value) {
// 这里是为了按照执行顺序放入值,保证顺序不会乱!
result[key] = value
index++;
if(index === array.length){
resolve(result)
}
}
// 这里为什么用for循环而不用
for(const i in array) {
let current = array[i]
if(current instanceof MyPromise){
// MyPromise对象
current.then(value => addData(i, value), reason => reject(reason))
}else {
// 非MyPromise对象
addData(i, current)
}
}
})
}
测试
function promise () {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
}, 2000)
})
}
function promise1 () {
return new MyPromise((resolve, reject) => {
resolve('成功1')
})
}
MyPromise.all(['a','b',promise(),promise1()]).then((res) => {
console.log(res)
})
预期输出: [ 'a', 'b', '成功', '成功1' ]
结果
Promise.resolve 方法的实现
Promise.resolve(value)
方法返回一个以给定值解析后的Promise
对象。如果这个值是一个 promise ,那么将返回这个 promise ;如果这个值是thenable(即带有"then"
方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。
该怎么实现呢?
首先先理清楚一下需求,Promise.resolve(value)
会判断value
是否Promise
对象如果是的话就直接返回,不是的话,我们需要包装一个Promise
对象,在进行返回,看起来是不是很简单?
上代码
static resolve (value) {
if(value instanceof MyPromise){
// MyPromise对象
return value
}else {
// 非MyPromise对象
return new MyPromise(resolve => resolve(value))
}
}
测试
测试用例
function promise () {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
}, 2000)
})
}
function promise1 () {
return new MyPromise((resolve, reject) => {
resolve('成功1')
})
}
MyPromise.resolve(100).then(res => console.log(res))
MyPromise.resolve(promise()).then(res => console.log(res))
MyPromise.resolve(promise1()).then(res => console.log(res))
预计输出: 100 成功1 成功
测试结果
妥!
finally
方法的实现
finally
方法有两个特点
- 不论
promise
对象状态是否成功finally
的回调函数都会执行 finally
后面可以链式调用then
方法获取返回值
如何实现呢?
首先我们可以确定 finally
他不是静态方法,所以我们进入文件中
finally(callback){
}
然后该怎么做呢??? 根据需求来一点一点实现
不论promise
对象状态是否成功 finally
的回调函数都会执行
那这就很简单,我们在调用 finally
时在函数内部也调用一下then
函数,不就好了吗?
还记得then
的两个参数吗? 我们同时传入,并调用我们的方法即可!
finally(callback){
this.then(() =>{
callback()
},() =>{
callback()
})
};
完美解决!
finally
后面可以链式调用then
方法获取返回值
这个我们就提过很多次了,可以链式调用then
那不就是返回了一个promise
对象吗?
让我们想想看then
中好像也返回了 这个对象那我们何尝不可返回这个then
呢?
finally(callback){
return this.then(() =>{
callback()
},() =>{
callback()
})
};
但是这样好像又不太对! 之后的链式调用好像获取不到参数诶怎么办?别慌!稍微在改造一些就好!
finally(callback){
return this.then(value =>{
callback()
return value
},reason =>{
callback()
throw reason
})
};
出现了一个问题
什么情况呢?
看看这个测试用代码:
function promise () {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('promise 成功')
}, 1000)
})
}
function promise1 () {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('promise1 成功')
}, 1000)
})
}
promise().finally(() => {
console.log('promise finally')
return promise1()
}).then(value => {
console.log(value)
},reason => {
console.log(reason)
})
我们的期望是先执行promise1
这个函数再去执行之后的then
,可是并没有这样执行
该怎么办呢?(动图截取不出来)
正纠结呢,我突然想到,如果我们可以等这个方法执行完毕再往后执行就好啦! 根据我们的执行方法是不是异步在进行处理,转换成promise
对象,那不就又可以进行之后的链式调用,还可以执行我们的方法???
resolve``Promise.resolve(value)
方法返回一个以给定值解析后的Promise
对象!!!!
那我们就可以通过它进行代码的修改了!
重新测试
这个测试没问题,不知道为啥我的动图截取不出来,大家将就着看我的描述吧!
测试结果没有问题,等待了1s之后才执行的,没毛病!
catch 方法的实现
catch
方法是为了处理Promise
方法失败的情况的函数,使用catch
就可以不用给then
方法传入失败的回调
实现
catch (failCallback) {
return this.then(undefined, failCallback)
};
就这么简单!
测试一下
测试代码
const MyPromise = require('./myPromise')
function promise () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise 成功')
}, 1000)
})
}
function promise1 () {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('promise1 失败')
}, 1000)
})
}
promise()
.then(val => console.log(val))
.catch(res => console.log(res))
promise1()
.then(val => console.log(val))
.catch(res => console.log(res))
结果
小结
到这里我们的手写代码就结束了
完整代码
const PENDING = "pending"; // 等待
const FULFILLED = "fulfilled"; // 成功
const REJECTED = "reject"; // 失败
const isFunction = (value) => typeof value === "function";
class MyPromise {
// Promise 状态
status = PENDING;
// 成功的值
value = undefined;
// 失败的值
reason = undefined;
// 成功回调
successCallback = [];
// 失败回调
failCallback = [];
// 执行器中需要捕获错误
constructor (callback) {
try {
callback(this.resolve, this.reject);
} catch (e) {
this.reject(e);
}
}
resolve = (value) => {
// 如果状态不是等待,阻止程序向下执行
if (this.status !== PENDING) {
return;
}
// 将状态更改为成功
this.status = FULFILLED;
// 保存成功的值
this.value = value;
// 判断成功回调是否存在,如果存在 调用
// this.successCallback && this.successCallback(this.value)
while (this.successCallback.length) {
// 调用shift方法然后每次执行的时候,推出方法出数组栈,并且将数据返回回去
this.successCallback.shift()();
}
};
reject = (value) => {
// 如果状态不是等待,阻止程序向下执行
if (this.status !== PENDING) {
return;
}
// 将状态更改为失败
this.status = REJECTED;
// 保存失败的值
this.reason = value;
// 判断失败回调是否存在,如果存在 调用
// this.failCallback && this.failCallback(this.reason)
while (this.failCallback.length) {
// 调用shift方法然后每次执行的时候,推出方法出数组栈,并且将数据返回回去
this.failCallback.shift()();
}
};
then (successCallback, failCallback) {
successCallback = isFunction(successCallback)
? successCallback
: (value) => value;
failCallback = isFunction(failCallback)
? failCallback
: (err) => {
throw err;
};
let thenPromise = new MyPromise((resolve, reject) => {
// 如果是成功的状态
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let x = successCallback(this.value);
// 判断x是否 MyPromise 类
// 是 就直接
resolvePromise(thenPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
// resolve(x)
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = failCallback(this.reason);
resolvePromise(thenPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
} else {
// 等待的情况
this.successCallback.push(() => {
setTimeout(() => {
try {
let x = successCallback(this.value);
resolvePromise(thenPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.failCallback.push(() => {
setTimeout(() => {
try {
let x = failCallback(this.reason);
resolvePromise(thenPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return thenPromise;
};
finally (callback) {
return this.then(value => {
return MyPromise.resolve(callback()).then(() => value)
}, reason => {
return MyPromise.resolve(callback()).then(() => { throw reason })
})
};
catch (failCallback) {
return this.then(undefined, failCallback)
};
static all (array) {
// 存储结果
let result = [];
let index = [];
return new MyPromise((resolve, reject) => {
function addData (key, value) {
// 这里是为了按照执行顺序放入值,保证顺序不会乱!
result[key] = value
index++;
if (index === array.length) {
resolve(result)
}
}
// 这里为什么用for循环而不用
for (const i in array) {
let current = array[i]
if (current instanceof MyPromise) {
// MyPromise对象
current.then(value => addData(i, value), reason => reject(reason))
} else {
// 非MyPromise对象
addData(i, current)
}
}
})
};
static resolve (value) {
if (value instanceof MyPromise) {
// MyPromise对象
return value
} else {
// 非MyPromise对象
return new MyPromise(resolve => resolve(value))
}
}
}
// 解析是否promise函数
// 不是直接调用reslove
// 是的话根据情况调用 reslove 或者 reject
function resolvePromise (thenPromise, value, resolve, reject) {
if (thenPromise === value) {
return reject(
new TypeError("Chaining cycle detected for promise #<MyPromise>")
);
}
if (value instanceof MyPromise) {
value.then(resolve, reject);
} else {
resolve(value);
}
}
module.exports = MyPromise;
看到这里了,给个赞吧!