Promise 是异步编程的一种解决方案。从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。
一般来说我们会碰到的回调嵌套都不会很多,一般就一到两级,但是某些情况下,回调嵌套很多时,代码就会非常繁琐,会给我们的编程带来很多的麻烦,这种情况俗称回调地狱。
function ajaxA() {
setTimeout(() => {
let resultA = 10
console.log("A请求返回结果===>", resultA)
}, 1000)
}
function ajaxB() {
setTimeout(() => {
let resultB = 20
console.log("B请求返回结果===>", resultB)
}, 1000)
}
function ajaxC() {
setTimeout(() => {
let resultC = 30
console.log("C请求返回结果===>", resultC)
}, 1000)
}
function ajaxD() {
setTimeout(() => {
let resultD = 40
console.log("D请求返回结果===>", resultD)
}, 1000)
}
//回调嵌套(回调地狱)
setTimeout(() => {
let resultA = 10
console.log("A请求返回结果===>", resultA)
setTimeout(() => {
let resultB = 20
console.log("B请求返回结果===>", resultB)
setTimeout(() => {
let resultC = 30
console.log("C请求返回结果===>", resultC)
setTimeout(() => {
let resultD = 40
console.log("D请求返回结果===>", resultD)
console.log("回调嵌套最终返回结果======>", resultA + resultB + resultC + resultD)
}, 1000)
}, 1000)
}, 1000)
}, 1000)
promise是用来解决两个问题的:
function ajaxA() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let resultA = 10
console.log("A_请求返回结果===>", resultA)
resolve(resultA)
}, 1000)
})
}
function ajaxB() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let resultB = 20
console.log("B_请求返回结果===>", resultB)
resolve(resultB)
}, 1000)
})
}
function ajaxC() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let resultC = 30
console.log("C_请求返回结果===>", resultC)
resolve(resultC)
}, 1000)
})
}
function ajaxD() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let resultD = 40
console.log("D_请求返回结果===>", resultD)
resolve(resultD)
}, 1000)
})
}
let resultA = 0
let resultB = 0
let resultC = 0
let resultD = 0
ajaxA().then(resA => {
resultA = resA
return ajaxB()
}).then(resB => {
resultB = resB
return ajaxC()
}).then(resC => {
resultC = resC
return ajaxD()
}).then(resD => {
resultD = resD
console.log("使用promise最终返回结果======>", resultA + resultB + resultC + resultD)
})
看到 A+ 肯定会想到是不是还有 A,事实上确实有。其实 Promise 有多种规范,除了前面的 Promise A、promise A+ 还有 Promise/B,Promise/D。目前我们使用的 Promise 是基于 Promise A+ 规范实现的,感兴趣的移步 promiseA+规范 了解一下,这里不赘述。
检验一份手写 Promise 靠不靠谱,通过 Promise A+ 规范自然是基本要求,这里我们可以借助 promises-aplus-tests 来检测我们的代码是否符合规范。
基础用法
const promise = new Promise((resolve, reject) => {
resolve('success')
reject('err')
})
promise.then(res => {
console.log('resolve', res)
}, err => {
console.log('reject', err)
})
分析
新建 MyPromise 类,传入执行器 executor
// myPromise.js
// 先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected'
class MyPromise {
status = PENDING; //status储存状态,初始值是 pending(等待)
value = null; //value成功后保存成功的结果
reason = null; //reason失败后保存失败的原因
constructor(executor) {
// executor是一个执行器,通过 new 命令创建对象实例时,自动调用该方法
// 并传入resolve和reject方法
executor(this.resolve, this.reject)
}
// resolve和reject为什么要用箭头函数?
// 如果直接调用的话,普通函数this指向的是window或者undefined
// 用箭头函数就可以让this指向当前实例对象
// 更改成功后的状态
resolve = (value) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
// 状态修改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
}
}
// 更改失败后的状态
reject = (reason) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
// 状态成功为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
}
}
// then方法传递两个回调函数作为参数
then(onFulfilled, onRejected) {
// 判断状态
if (this.status === FULFILLED) {
// 调用成功回调,并且把值返回
onFulfilled(this.value);
} else if (this.status === REJECTED) {
// 调用失败回调,并且把原因返回
onRejected(this.reason);
}
}
}
// 使用 module.exports 对外暴露 MyPromise 类
module.exports = MyPromise;
测试
//test.js
// 引入我们的 MyPromise.js
const MyPromise = require('./MyPromise')
const promise = new MyPromise((resolve, reject) => {
resolve('success')
reject('err')
})
promise.then(value => {
console.log('resolve', value)
}, reason => {
console.log('reject', reason)
})
// 执行结果
// resolve success
上面执行同步代码时没有问题,但是如果含有异步任务就会出现问题,例如:
//test.js
const MyPromise = require('./MyPromise')
const myPromiseTest = new MyPromise((resolve, reject) => {
setTimeout(()=>{
resolve('success')
},1000)
})
myPromiseTest.then(value => {
console.log('resolve', value)
}, reason => {
console.log('reject', reason)
})
//此时不会有打印信息
原因分析:
由于 主线程代码立即执行,而 setTimeout 是异步代码,会隔一段时间才执行resolve方法从而去改变promise的状态,所以此时promise的状态为Pending(等待),但是then方法里面并没有对状态为Pending时的情况做判断。
代码改造:
1、MyPromise 类中新增两个变量分别用于存储成功和失败的回调
// 存储成功回调函数
onFulfilledCallback = null;
// 存储失败回调函数
onRejectedCallback = null;
2、then 方法中对 Pending 状态进行处理
then(onFulfilled, onRejected) {
// 判断状态
if (this.status === FULFILLED) {
// 调用成功回调,并且把值返回
onFulfilled(this.value);
} else if (this.status === REJECTED) {
// 调用失败回调,并且把原因返回
onRejected(this.reason);
} else if (this.status === PENDING) {
// ==== 新增 ====
// 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来
// 等到执行成功失败函数的时候再传递
this.onFulfilledCallback = onFulfilled;
this.onRejectedCallback = onRejected;
}
}
3、在resolve 与 reject 中调用回调函数
// 更改成功后的状态
resolve = (value) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
// 状态修改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
// ==== 新增 ====
// 判断成功回调是否存在,如果存在就调用
this.onFulfilledCallback && this.onFulfilledCallback(value);
}
}
// 更改失败后的状态
reject = (reason) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
// 状态成功为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
// ==== 新增 ====
// 判断失败回调是否存在,如果存在就调用
this.onRejectedCallback && this.onRejectedCallback(reason)
}
}
代码测试(处理异步任务)
const MyPromise = require('./MyPromise')
const myPromiseTest = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 2000);
})
myPromiseTest.then(value => {
console.log('resolve', value)
}, reason => {
console.log('reject', reason)
})
// 等待 2s 输出 resolve success
改造完以后发现可以处理异步任务了,但是由于then方法可以多次调用,按照上面的写法一旦遇到多个异步任务时就会出现问题,例如:
const MyPromise = require('./MyPromise')
const myPromiseTest = new MyPromise((resolve, reject) => {
//resolve('同步success!')
setTimeout(() => {
resolve('异步success!')
}, 2000);
})
myPromiseTest.then(value => {
console.log('这是第一个then方法!')
console.log('resolve', value)
})
myPromiseTest.then(value => {
console.log('这是第二个then方法!')
console.log('resolve', value)
})
myPromiseTest.then(value => {
console.log('这是第三个then方法!')
console.log('resolve', value)
})
// 执行结果
// 这是第三个then方法!
// resolve success!
原因分析:
由于只设置了一个变量用于保存回调函数,所以在多个then方法调用的时候,用于存放回调函数的变量在赋值的时候后面的会把前面的覆盖,导致最终resolve执行取到的回调函数则是最后一个then方法里面的内容。分析下来需要改进的地方就是分别保存每个异步任务的回调。
代码改造:
1、由于可能存在多个异步任务回调,用数组的形式保存
// 存储成功回调函数
// onFulfilledCallback = null;
onFulfilledCallbacks = [];
// 存储失败回调函数
// onRejectedCallback = null;
onRejectedCallbacks = [];
2、回调函数存入数组中
then(onFulfilled, onRejected) {
// 判断状态
if (this.status === FULFILLED) {
// 调用成功回调,并且把值返回
onFulfilled(this.value);
} else if (this.status === REJECTED) {
// 调用失败回调,并且把原因返回
onRejected(this.reason);
} else if (this.status === PENDING) {
// 因为不知道后面状态的变化,这里先将成功回调和失败回调存储起来
// 等待后续调用
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}
}
3、循环调用成功和失败的回调
// 更改成功后的状态
resolve = (value) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
// 状态修改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
// resolve里面将所有成功的回调拿出来执行
while (this.onFulfilledCallbacks.length) {
// Array.shift() 取出数组第一个元素,然后()调用,shift不是纯函数,取出后,数组将失去该元素,直到数组为空
this.onFulfilledCallbacks.shift()(value)
}
}
}
// 更改失败后的状态
reject = (reason) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
// 状态成功为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
// ==== 新增 ====
// resolve里面将所有失败的回调拿出来执行
while (this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(reason)
}
}
}
再次测试运行
const MyPromise = require('./MyPromise')
const myPromiseTest = new MyPromise((resolve, reject) => {
//resolve('同步success!')
setTimeout(() => {
resolve('异步success!')
}, 2000);
})
myPromiseTest.then(value => {
console.log('这是第一个then方法!')
console.log('resolve', value)
})
myPromiseTest.then(value => {
console.log('这是第二个then方法!')
console.log('resolve', value)
})
myPromiseTest.then(value => {
console.log('这是第三个then方法!')
console.log('resolve', value)
})
// 执行结果
// 这是第一个then方法!
// resolve success!
// 这是第二个then方法!
// resolve success!
// 这是第三个then方法!
// resolve success!
原生 promise 案例:
const promiseTest = new Promise((resolve, reject) => {
resolve('success!')
})
promiseTest.then(res => {
console.log("第一个then返回结果", res)
return 'other success!'
}).then(res => {
console.log("第二个then返回结果", res)
})
// 执行结果
// 第一个then返回结果 success!
// 第二个then返回结果 other success!
myPromise 测试:
const myPromiseTest = new MyPromise((resolve, reject) => {
resolve('success!')
})
myPromiseTest.then(res => {
console.log("第一个then返回结果",res)
return 'other success!'
}).then(res=>{
console.log("第二个then返回结果",res)
})
// 执行结果
// (报错信息) TypeError: Cannot read property 'then' of undefined
then 方法要实现链式调用,就需要方法里面返回一个 Promise 对象。then 方法里面 return 一个返回值作为下一个 then 方法的参数,如果是 return 一个 Promise 对象,那么就需要判断它的状态
代码改造:
class MyPromise {
//......
then(onFulfilled, onRejected) {
// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
const newPromise = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
const x = onFulfilled(this.value);
resolve(x)
} else if (this.status === REJECTED) {
// ......
} else {
// ......
}
})
return newPromise;
}
}
修改后再次测试:
const myPromiseTest = new MyPromise((resolve, reject) => {
resolve('success!')
})
myPromiseTest.then(res => {
console.log("第一个then返回结果",res)
return 'other success!'
}).then(res => {
console.log("第二个then返回结果",res)
})
// 执行结果
// 第一个then返回结果 success!
// 第二个then返回结果 other success!
判断当前then回调的的执行结果值是普通对象还是Promise对象
代码修改:
class MyPromise {
......
then(onFulfilled, onRejected) {
const newPromise = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
const x = onFulfilled(this.value);
// 将执行结果传入 resolvePromise 集中处理
resolvePromise(x, resolve, reject);
} else if (this.status === REJECTED) {
// ......
} else if (this.status === PENDING) {
// ......
}
})
return newPromise;
}
}
function resolvePromise(x, resolve, reject) {
// 判断当前then回调的执行结果值是普通对象还是Promise对象
// 如果是普通值,直接调用resolve
// 如果是Promise对象,查看Promise对象返回的状态,根据状态决定调用resolve还是reject
if (x instanceof MyPromise) {
// 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
// x.then(value => resolve(value), reason => reject(reason))
// 简化之后
x.then(resolve, reject)
} else {
// 普通值
resolve(x)
}
}
**注意点:then回调的执行结果返回的promise不可以是它本身,会发生循环调用 , 这个时候程序就会报错 **
原生 promise 案例:
const promiseTest = new Promise((resolve, reject) => {
resolve(100)
})
const p1 = promiseTest.then(res => {
console.log(res)
return p1
})
// 执行结果
// 100
// (报错信息) Uncaught (in promise) TypeError: Chaining cycle detected for promise #
myPromise 实现
class MyPromise {
......
then(onFulfilled, onRejected) {
const newPromise = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
const x = onFulfilled(this.value);
// resolvePromise 集中处理,将 newPromise 传入
resolvePromise(newPromise, x, resolve, reject);
} else if (this.status === REJECTED) {
// ......
} else if (this.status === PENDING) {
// ......
}
})
return newPromise;
}
}
function resolvePromise(newPromise, x, resolve, reject) {
// 如果返回的 promise 对象是它本身,抛出类型错误
if (newPromise === x) {
reject(new TypeError("Chaining cycle detected for promise #" ));
}
if (x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
}
运行一下:
const myPromiseTest = new MyPromise((resolve, reject) => {
resolve(100)
})
const p1 = myPromiseTest.then(res => {
console.log(res)
return p1
})
// 执行结果
// 100
// (报错信息) Uncaught ReferenceError: Cannot access 'p1' before initialization
报错信息提示 p1 未初始化,原因在于 resolvePromise(newPromise, x, resolve, reject) 传递的参数 newPromise 此时还未初始化完成,所以这里用一个 宏任务或者微任务 来创建一个异步函数去等待 newPromise 完成初始化
代码修改:
then(onFulfilled, onRejected) {
// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
const promise = new MyPromise((resolve, reject) => {
// 这里的内容在执行器中,会立即执行
if (this.status === FULFILLED) {
// 创建一个宏任务(setTimeout)等待 promise 完成初始化
setTimeout(()=>{
// 获取成功回调函数的执行结果
const x = onFulfilled(this.value);
// 传入 resolvePromise 集中处理
resolvePromise(promise, x, resolve, reject);
},0)
} else if (this.status === REJECTED) {
// ......
} else if (this.status === PENDING) {
// ......
}
})
return promise;
}
修改后重新运行
const myPromiseTest = new MyPromise((resolve, reject) => {
resolve(100)
})
const p1 = myPromiseTest.then(res => {
console.log(res)
return p1
})
// 执行结果和原生promise一致
// 100
// (报错信息) Uncaught (in promise) TypeError: Chaining cycle detected for promise #
捕获执行器中的代码,如果执行器中有代码错误,那么 Promise 的状态要变为失败
constructor(executor) {
try {
// executor是一个执行器,进入会立即执行
// 并传入resolve和reject方法
executor(this.resolve, this.reject)
} catch (error) {
// 如果有错误,就直接执行 reject
this.reject(error)
}
}
测试:
const myPromiseTest = new MyPromise((resolve, reject) => {
throw new Error('执行器错误')
})
myPromiseTest.then(res => {
console.log(res)
},err => {
console.log(err)
})
// 执行结果
// Error: 执行器错误
then(onFulfilled, onRejected) {
// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
const promise = new MyPromise((resolve, reject) => {
// 这里的内容在执行器中,会立即执行
if (this.status === FULFILLED) {
// 创建一个宏任务(setTimeout)等待 promise 完成初始化
setTimeout(() => {
try {
// 获取成功回调函数的执行结果
const x = onFulfilled(this.value);
// 传入 resolvePromise 集中处理
resolvePromise(newPromise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === REJECTED) {
// ......
} else if (this.status === PENDING) {
// ......
}
})
return promise;
}
测试:( 第一个then方法中的错误要在第二个then方法中捕获到 )
const myPromiseTest = new MyPromise((resolve, reject) => {
resolve("success")
})
myPromiseTest.then(res => {
console.log("第1个then方法返回结果===>", res)
throw new Error('error!!!')
}, err => {
console.log("第1个then方法返回错误信息===>", err)
}).then(res => {
console.log("第2个then方法返回结果===>", res)
}, err => {
console.log("第2个then方法返回错误信息===>", err)
})
// 执行结果
// 第1个then方法返回结果===> success
// 第2个then方法返回错误信息===> Error: error!!!
根据 fulfilled 状态下的处理,对 rejected 和 pending 状态进行改造
then(onFulfilled, onRejected) {
const newPromise = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(newPromise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(newPromise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === PENDING) {
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(newPromise, x, resolve, reject);
} catch (error) {
reject(error)
}
},0)
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(newPromise, x, resolve, reject);
} catch (error) {
reject(error)
}
},0)
});
}
})
return newPromise;
}
以上写的都是默认传入 onFulfilled、onRejected 两个回调函数,但是实际上原生 Promise 是可以选择参数的单传或者不传,都不会影响执行。
例如:
new Promise((resolve, reject) => {
resolve("success")
}).then().then().then().then(res => console.log(res))
// 执行结果 success
then 方法改造
then(onFulfilled, onRejected) {
// 成功函数处理 忽略函数之外的其他值
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
// 失败函数处理 忽略函数之外的其他值 抛出异常 实现catch冒泡的关键
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
const newPromise = new MyPromise((resolve, reject) => {
// ......
})
}
测试:
// 验证resolve
new MyPromise((resolve, reject) => {
resolve("resolve success")
}).then().then().then().then(res => console.log("结果===>", res))
// 执行结果
// 结果===> resolve success
// 验证reject
new MyPromise((resolve, reject) => {
reject("reject error")
}).then().then().then().then(res => console.log("结果===>", res), err => console.log("错误===>", err))
// 执行结果
// 错误===> reject error
原生 promise 案例:
Promise.resolve().then(res => {
console.log("two")
})
console.log("one")
// 执行结果
// one
// two
Promise.resolve()用于将现有对象转换为Promise对象,从而控制异步流程。
代码实现
// resolve 静态方法
static resolve(value) {
// 如果是MyPromise 实例 则直接返回
if (value instanceof MyPromise) return value;
// 如果是MyPromise 实例 否则返回一个 MyPromise实例
return new MyPromise((resolve) => resolve(value));
}
// reject 静态方法
static reject(reason) {
// 如果是MyPromise 实例 则直接返回
if (reason instanceof MyPromise) return reason;
// 如果是MyPromise 实例 否则返回一个 MyPromise实例
return new MyPromise((resolve, reject) => reject(reason));
}
Promise.all:
Promise.all 可以将多个 Promise 实例包装成一个新的 Promise 实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被 reject 失败状态的值。Promise.all 获得的成功结果的数组里面的数据顺序和 Promise.all 接收到的数组顺序是一致的,即 p1 的结果在前,即便 p1 的结果获取的比 p2 要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all 毫无疑问可以解决这个问题。
Promise.race
顾名思义,Promse.race 就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3]) 里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。 该方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
let p1 = Promise.resolve(42)
p1.then((value) => {
//第一种情况,返回一个Promise
return new Promise(function(resolve, rejected) {
resolve(value + 1)
})
//第二种情况,返回一个值
return value + 2;
//第三种情况,新建一个promise,使用reslove返回值
const p2 = new Promise(function(resolve, rejected) {
resolve(value + 3)
})
//第四种情况,新建一个promise,使用return返回值
const p2 = new Promise(function(resolve, rejected) {
return (value + 4)
})
//第五种情况,没有返回值
return undefined
}).then((value) => {
console.log(value)
})
按照学习理解:
第一种情况,新建promise的resolve传出的值将作为then方法返回的promise的resolve的值传递出,console将打印出43
第二种情况,return的值将作为then方法返回的promise的resolve的值传递出,console将打印出44
第三种情况,虽然新建了promise,但对于then方法来说,没有向它返回的promise传递返回值,console将打印出undifined
第四种情况,同第三种情况,
第五种情况,then方法没有返回值,then方法的promise的resolve的值将传递出undifined。
以上三、四、五种情况,其实都是一样的问题,构造then方法的函数没有向then方法返回的promise对象的resolve方法传递值。因此resolve返回的都是undfined
// 先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected'
class MyPromise {
status = PENDING; //status储存状态,初始值是 pending(等待)
value = null; //value成功后保存成功的结果
reason = null; //reason失败后保存失败的原因
onFulfilledCallbacks = [];// 存储成功回调函数
onRejectedCallbacks = [];// 存储失败回调函数
constructor(executor) {
try {
// executor是一个执行器,进入会立即执行
// 并传入resolve和reject方法
executor(this.resolve, this.reject)
} catch (error) {
// 如果有错误,就直接执行 reject
this.reject(error)
}
}
// resolve和reject为什么要用箭头函数?
// 如果直接调用的话,普通函数this指向的是window或者undefined
// 用箭头函数就可以让this指向当前实例对象
// 更改成功后的状态
resolve = (value) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
// 状态修改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
// ==== 新增 ====
// resolve里面将所有成功的回调拿出来执行
while (this.onFulfilledCallbacks.length) {
// Array.shift() 取出数组第一个元素,然后()调用,shift不是纯函数,取出后,数组将失去该元素,直到数组为空
this.onFulfilledCallbacks.shift()(value)
}
}
}
// 更改失败后的状态
reject = (reason) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
// 状态成功为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
// ==== 新增 ====
// resolve里面将所有失败的回调拿出来执行
while (this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(reason)
}
}
}
// then方法传递两个回调函数作为参数
then(onFulfilled, onRejected) {
// 成功函数处理 忽略函数之外的其他值
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
// 失败函数处理 忽略函数之外的其他值 抛出异常 实现catch冒泡的关键
onRejected = typeof onRejected === 'function' ? onRejected : reason => {
throw reason
}
// 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
const newPromise = new MyPromise((resolve, reject) => {
// 这里的内容在执行器中,会立即执行
if (this.status === FULFILLED) {
// 创建一个宏任务(setTimeout)等待promise完成初始化
setTimeout(() => {
try {
// 获取成功回调函数的执行结果
const x = onFulfilled(this.value);
// 传入 resolvePromise 集中处理
resolvePromise(newPromise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
// 获取失败回调函数的执行结果
const x = onRejected(this.reason);
// 传入 resolvePromise 集中处理
resolvePromise(newPromise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === PENDING) {
// 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来
// 等到执行成功失败函数的时候再传递
this.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
// 获取成功回调函数的执行结果
const x = onFulfilled(this.value);
// 传入 resolvePromise 集中处理
resolvePromise(newPromise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
// 调用失败回调,并且把原因返回
const x = onRejected(this.reason);
// 传入 resolvePromise 集中处理
resolvePromise(newPromise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
});
}
})
return newPromise;
}
// resolve 静态方法
static resolve(value) {
// 如果是MyPromise 实例 则直接返回
if (value instanceof MyPromise) return value;
// 如果是MyPromise 实例 否则返回一个 MyPromise实例
return new MyPromise((resolve) => resolve(value));
}
// reject 静态方法
static reject(reason) {
// 如果是MyPromise 实例 则直接返回
if (reason instanceof MyPromise) return reason;
// 如果是MyPromise 实例 否则返回一个 MyPromise实例
return new MyPromise((resolve, reject) => reject(reason));
}
}
function resolvePromise(newPromise, x, resolve, reject) {
// 判断当前then回调的执行结果值是普通对象还是Promise对象
// 如果是普通值,直接调用resolve
// 如果是Promise对象,查看Promise对象返回的状态,根据状态决定调用resolve还是reject
// 如果return的Promise是它自己,抛出类型错误并返回
if (newPromise === x) {
reject(new TypeError("Chaining cycle detected for promise #" ));
}
if (x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
}
module.exports = MyPromise;
/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/
// 初始状态
const PENDING = "pending";
// 完成状态
const FULFILLED = "fulfilled";
// 失败状态
const REJECTED = "rejected";
// 异步执行方法封装
function asyncExecFun(fn) {
setTimeout(() => fn(), 0);
}
// 执行promise resolve功能
function resolvePromise(promise, res, resolve, reject) {
// 返回同一个promise
if (promise === res) {
reject(new TypeError("Chaining cycle detected for promise #" ));
return;
}
// promise结果
if (res instanceof MyPromise) {
res.then(resolve, reject);
} else {
// 非promise结果
resolve(res);
}
}
/**
* 1. 是个构造函数
* 2. 传入一个可执行函数 函数的入参第一个为 fullFill函数 第二个为 reject函数; 函数立即执行, 参数函数异步执行
* 3. 状态一旦更改就不可以变更 只能 pending => fulfilled 或者 pending => rejected
* 4. then 的时候要处理入参的情况 successCallback 和failCallback 均可能为非函数
* 默认的 failCallback 一定要将异常抛出, 这样下一个promise便可将其捕获 异常冒泡的目的
* 5. then 中执行回调的时候要捕获异常 将其传给下一个promise
* 如果promise状态未变更 则将回调方法添加到对应队列中
* 如果promise状态已经变更 需要异步处理成功或者失败回调
* 因为可能出现 回调结果和当前then返回的Promise一致 从而导致死循环问题
* 6. catch只是then的一种特殊的写法 方便理解和使用
* 7. finally 特点 1. 不过resolve或者reject都会执行
* 2. 回调没有参数
* 3. 返回一个Promise 且值可以穿透到下一个then或者catch
* 8. Promise.resolve, Promise.reject 根据其参数返回对应的值 或者状态的Promise即可
* 9. Proise.all 特点 1. 返回一个Promise
* 2. 入参是数组 resolve的情况下出参也是数组 且结果顺序和调用顺序一致
* 3. 所有的值或者promise都完成才能resolve 所有要计数
* 4. 只要有一个为reject 返回的Promise便reject
* 10. Proise.race 特点 1. 返回一个Promise
* 2. 入参是数组 那么出参根据第一个成功或者失败的参数来确定
* 3. 只要有一个resolve 或者reject 便更改返回Promise的状态
*
*
*/
class MyPromise {
status = PENDING;
value = undefined;
reason = undefined;
successCallbacks = [];
failCallbacks = [];
constructor(exector) {
// 立即执行传入参数
// 参数直接写为 this.resolve 会导致函数内 this指向会发生改变
// 异步执行状态变更
// 捕获执行器的异常
try {
exector(
(value) => asyncExecFun(() => this.resolve(value)),
(reason) => asyncExecFun(() => this.reject(reason))
);
} catch (e) {
this.reject(e)
}
}
resolve(value) {
// 如果状态已经变更则直接返回
if (this.status !== PENDING) return;
this.value = value;
this.status = FULFILLED;
// 执行所有成功回调
while (this.successCallbacks.length) this.successCallbacks.shift()();
}
reject(reason) {
// 如果状态已经变更则直接返回
if (this.status !== PENDING) return;
this.reason = reason;
this.status = REJECTED;
if (!this.failCallbacks.length) {
throw '(in MyPromise)'
}
// 执行所有失败回调
while (this.failCallbacks.length) this.failCallbacks.shift()();
}
then(successCallback, failCallback) {
// 成功函数处理 忽略函数之外的其他值
successCallback = typeof successCallback == "function" ? successCallback : (v) => v;
// 失败函数处理 忽略函数之外的其他值 抛出异常 实现catch冒泡的关键
failCallback = typeof failCallback == "function" ? failCallback : (reason) => {
throw reason
};
let promise = new MyPromise((resolve, reject) => {
// 统一异常处理逻辑
const execFun = (fn, val) => {
try {
let res = fn(val);
resolvePromise(promise, res, resolve, reject);
} catch (e) {
reject(e);
}
};
// 执行成功回调
const execSuccessCallback = () => execFun(successCallback, this.value);
// 执行失败回调
const execFailCallback = () => execFun(failCallback, this.reason);
// 同步将对应成功或者失败回调事件加入对应回调队列
if (this.status === PENDING) {
// 将成功回调加入队列
this.successCallbacks.push(execSuccessCallback);
// 讲失败回调加入队列
this.failCallbacks.push(execFailCallback);
return;
}
// 延迟执行 可以将函数执行结果和当前then 返回的promise 进行比较
asyncExecFun(() => {
// 如果已经 fulfilled 可直接调用成功回调方法
if (this.status === FULFILLED) {
execSuccessCallback();
// 如果已经 rejected 可直接调用失败回调方法
} else if (this.status === REJECTED) {
execFailCallback();
}
});
});
return promise;
}
catch (failCallback) {
return this.then(undefined, failCallback)
}
finally(callback) {
return this.then(
// 穿透正常值
(value) => MyPromise.resolve(callback()).then(() => value),
(reason) =>
MyPromise.resolve(callback()).then(() => {
// 穿透异常信息
throw reason;
})
)
}
static resolve(value) {
// 如果是MyPromise 实例 则直接返回
if (value instanceof MyPromise) return value;
// 如果是MyPromise 实例 否则返回一个 MyPromise实例
return new MyPromise((resolve) => resolve(value));
}
static reject(reason) {
// 如果是MyPromise 实例 则直接返回
if (reason instanceof MyPromise) return reason;
// 如果是MyPromise 实例 否则返回一个 MyPromise实例
return new MyPromise((resolve, reject) => reject(reason));
}
// all方法
static all(array) {
// 存储结果
let result = [];
// 存储数组长度
let len = array.length;
// 创建返回MyPromise
let promise = new MyPromise((resolve, reject) => {
// 定义当前MyPromise的索引
let index = 0;
// 添加数据的公用方法
function addData(key, data) {
// 赋值
result[key] = data;
// 索引递增
index++;
// 全部执行完则resolve
if (index == len) {
resolve(result);
}
}
// 按顺序变量数组
for (let i = 0; i < len; i++) {
let curr = array[i];
// 如果是MyPromise则 按其规则处理
if (curr instanceof MyPromise) {
curr.then((value) => addData(i, value), reject);
} else {
// 非MyPromise直接赋值
addData(i, curr);
}
}
});
// 返回新的MyPromise实例
return promise;
}
// 只要有一个成功或者失败就返回
static race(array) {
let promise = new MyPromise((resolve, reject) => {
for (let i = 0; i < array.length; i++) {
let curr = array[i];
// MyPromise实例 结果处理
if (curr instanceof MyPromise) {
curr.then(resolve, reject);
} else {
// 非MyPromise实例处理
resolve(curr);
}
}
});
return promise;
}
}
module.exports = MyPromise;