前言
大家好,我是NobodyDJ,一名默默无闻的前端学徒。
Promise是我在学习JavaScript里,比较疑惑的知识点,一直在死记硬背它的用法,不懂其内部实现原理,不求甚解的状态,导致我日常开发中只会机械化地使用它们。因此,我整理一些Promise内部的工作原理,来帮助自己梳理其中的运作过程 ,希望也能给你一些帮助。
Promise基本用法的原理
resolve和reject
首先,es6规定,Promise对象是一个构造函数,用来生成Promise实例。以下创建了一个Promise实例:
let promise = new Promise(function(resolve, reject){
// ...some code
})
如上,代码所示,Promise构造函数主要接收了两个参数,一个是resolve函数,一个是reject函数。我们知道当执行resolve方法时,Promise对象的状态由“未完成”转变为“成功”(pending=>resolved);当执行resolve方法时,Promise对象的状态由"未完成"转变为“失败”(pending=>rejected),并且Promise对象状态一旦发生了以上两种之一的转变,就不会再改变了(被称为状态凝固),现在来看看Promise内部是如何实现resolve()方法和reject()方法。
关键问题
从刚刚的描述中,我们可以知道有如下几个关键问题:
- Promise对象一开始是处于pending状态的。
- 调用resolve()方法后,Promise的状态由pending转变为resolved。
- 调用reject()方法后,Promise的状态由pending转变为rejected。
- Promise的状态一旦发送了改变,就不能再改变了。
实现代码
具体实现的代码如下:
class MyPromise{
constructor(executor){
// 初始化
this.initValue();
// 初始化this绑定
this.initBind();
// 执行Promise构造函数传进来的函数,try/catch是对throw方法错误异常的处理
try{
executor(this.resolve, this.reject)
}catch(e){
this.reject(e)
}
}
// 初始化方法
initValue(){
this.PromiseResult = null;
this.PromiseState = 'pending'
}
// 初始化this绑定的方法
initBind(){
// 将原型对象的方法始终绑定在实例上
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
// resolve方法
resolve(value) {
// 这里实现了Promise对象无法改变的功能
if (this.PromiseState !== 'pending') return
this.PromiseState = 'fulfilled';
this.PromiseResult = value;
}
// reject方法
reject(reason) {
if (this.PromiseState !== 'pending') return
this.PromiseState = 'rejected'
this.PromiseResult = reason
}
}
测试用例
测试用例分别对应了以上4个关键字
// 执行resolve()方法
const test1 = new MyPromise((resolve, reject) => {
resolve('成功')
})
console.log(test1)
// 执行reject()方法
const test2 = new MyPromise((resolve, reject) => {
reject('失败')
})
console.log(test2)
// 检验Promise对象的状态能否被再次改变
const test3 = new MyPromise((resolve, reject) => {
reject('失败')
resolve('成功')
})
console.log(test3)
// 检验Promise实例化时,抛出错误时
const test4 = new MyPromise((resolve, reject) => {
throw ('失败')
})
console.log(test4)
测试结果如下:
then方法
Promise方法是存在于Promise原型对象Promise.prototype上的。它的作用是为Promise实例添加状态改变时的回调函数。
关键问题
主要有以下几个关键点:
- then方法接收两个回调函数,一个是成功的回调,另一个是失败的回调。
- 当执行完resolve函数,Promise状态变为resolved此时执行成功的回调;当执行完reject函数,Promise状态变为rejected状态,此时,执行失败的回调。
- resolve与reject存在异步操作时,必须等到执行完resolve与reject之后,才能调用then方法
- then方法支持链式调用,then方法会接收上一次then方法返回值的影响。
代码实现
首先,实现第一个与第二个关键点,根据Promise的状态分别执行对应的回调函数
class MyPromise{
constructor(executor){
// 初始化
this.initValue();
// 初始化this绑定
this.initBind();
// 执行Promise构造函数传进来的函数,try/catch是对throw方法错误异常的处理
try{
executor(this.resolve, this.reject)
}catch(e){
this.reject(e)
}
}
// 初始化方法
initValue(){
this.PromiseResult = null;
this.PromiseState = 'pending'
}
// 初始化this绑定的方法
initBind(){
// 将原型对象的方法始终绑定在实例上
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
// resolve方法
resolve(value) {
// 这里实现了Promise对象无法改变的功能
if (this.PromiseState !== 'pending') return
this.PromiseState = 'fulfilled';
this.PromiseResult = value;
}
// reject方法
reject(reason) {
if (this.PromiseState !== 'pending') return
this.PromiseState = 'rejected'
this.PromiseResult = reason
}
// then方法
then(onFulfilled, onRejected){
// 接收两个回调函数onFulfilled, onRejected
// 进行参数判断,确保是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
// 随后就是判断Promise的状态分别执行对应的回调
if (this.PromiseState === 'fulfilled') {
onFulfilled(this.PromiseResult)
}else if(this.PromiseState === 'rejected'){
onRejected(this.PromiseResult)
}
}
}
其次,来实现Promise中的异步操作,只要当Promise状态变为fulfilled或rejected状态之一时,才会执行对应的onFulfilled或onRejected方法,当Promise处于pending状态时,那就表示Promise还没有执行完毕,等待状态发生变化时,才能对应的回调函数,因此,我们可以使用数组来保存这些未执行的回调函数。
关键点:
- then方法判断Promise的状态。
- 保存那些将要执行的回调函数。
class MyPromise{
constructor(executor){
// 初始化
this.initValue();
// 初始化this绑定
this.initBind();
// 执行Promise构造函数传进来的函数,try/catch是对throw方法错误异常的处理
try{
executor(this.resolve, this.reject)
}catch(e){
this.reject(e)
}
}
// 初始化方法
initValue(){
this.PromiseResult = null;
this.PromiseState = 'pending';
this.onFulfilledCallbacks = [];// 保存成功的回调
this.onRejectedCallbacks = []; // 保存失败的回调
}
// 初始化this绑定的方法
initBind(){
// 将原型对象的方法始终绑定在实例上
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
// resolve方法
resolve(value) {
// 这里实现了Promise对象无法改变的功能
if (this.PromiseState !== 'pending') return
this.PromiseState = 'fulfilled';
this.PromiseResult = value;
this.onFulfilledCallbacks.forEach(fn=>fn(this.PromiseResult))
}
// reject方法
reject(reason) {
if (this.PromiseState !== 'pending') return
this.PromiseState = 'rejected';
this.PromiseResult = reason;
this.onRejectedCallbacks.forEach(fn=>fn(this.PromiseResult))
}
// then方法
then(onFulfilled, onRejected){
// 接收两个回调函数onFulfilled, onRejected
// 进行参数判断,确保是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
// 随后就是判断Promise的状态分别执行对应的回调
if (this.PromiseState === 'fulfilled') {
onFulfilled(this.PromiseResult)
}else if(this.PromiseState === 'rejected'){
onRejected(this.PromiseResult)
}else if(this.PromiseState === 'pending'){
this.onFulfilledCallbacks.push(onFulfilled);// 保存成功的回调
this.onRejectedCallbacks.push(onRejected);// 保存失败的回调
}
}
}
最后,是Promise的链式调用,链式调用的核心要点如下:
- 上一个then函数要返回一个Promise对象
- 下个then的参数,要拿到上一个then函数的回调返回值
- 对回调的返回值进行类型判断,如果是Promise对象,则要对该对象的状态判断执行哪一种方法,如果是其他数据类型则直接执行resolve方法。
- 不可以重复返回对象本身
以下代码暂时展示了具体思路,只展示了resolve()方法的改写
then(onFulfilled, onRejected){
// 接收两个回调函数onFulfilled, onRejected
// 进行参数判断,确保是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
// 返回Promise状态
let thenPromise= new MyPromise((resolve,reject)=>{
// 随后就是判断Promise的状态分别执行对应的回调
// 注意这里有个坑!
// 如果链式调用循环返回同一个Promise,会导致第二Promise对象没有生成就要传入resolvePromise方法
// 这是因为这是个异步操作,要等到同步操作执行完毕再执行,增加一个setTimeout
if (this.PromiseState === 'fulfilled') {
setTimeOut(()=>{
let x = onFulfilled(this.PromiseResult);
// 这里对返回值x进行判断,如果是Promise对象要判断Promise的状态
// 如果是一个普通值,直接用resolve方法返回
resolvePromise(x,resolve,reject,thenPromise);
},0)
}else if(this.PromiseState === 'rejected'){
onRejected(this.PromiseResult)
}else if(this.PromiseState === 'pending'){
this.onFulfilledCallbacks.push(onFulfilled);// 保存成功的回调
this.onRejectedCallbacks.push(onRejected);// 保存失败的回调
}
})
return thenPromise
}
function resolvePromise(x, resolve, reject,thenPromise){
// 防止循环调用Promise,不可以返回本身
if(x === thenPromise){
return reject(new TypeError('不可以返回本身!'))
}
// 然后判断这个x是否是一个Promise对象,可以使用instanceOf方法
if(x instanceof MyPromise){
// 完整写法
// x.then((value)=>{
// resolve(value);
// },err=>{
// reject(err);
// })
x.then(resolve, reject)
}else{
// 普通值
resolve(x)
}
}
测试用例
- 测试Promise处于fulfilled状态下执行onFulfiled方法和处于rejected状态下执行onRejected方法
const test5 = new MyPromise((resolve, reject)=>{
resolve('成功')
}).then(res => console.log(res), err => console.log(err))
const test6 = new MyPromise((resolve, reject)=>{
reject('失败')
}).then(res => console.log(res), err => console.log(err))
测试结果如下:
- 测试Promise异步操作
const test7 = new MyPromise((resolve, reject)=>{
setTimeout(() => {
resolve('成功')
}, 1000);
}).then(res=>console.log(res), err => console.log(err))
测试结果如下:
- then方法的链式调用
测试用例如下:
const test8 = new MyPromise((resolve, reject) => {
resolve(500)
}).then((res) => {
console.log(res)
return 200
}).then((data) => {
console.log(data)
})
const test9 = new MyPromise((resolve, reject) => {
resolve(500)
}).then((res) => {
console.log(res)
return new MyPromise((resolve, reject) => {
resolve(200)
})
}).then((data) => {
console.log(data)
})
// 链式调用时,返回本身
let p = new MyPromise((resolve, reject) => {
resolve(300)
})
let p2 = p.then((res) => {
console.log(res)
return p2
})
p2.then((res) => {
console.log(res)
})
测试结果:
实现Promise的完整代码
class MyPromise{
constructor(executor){
console.log(executor)
// 初始化值
this.initValue()
// 初始化this执行
this.initBind()
// 执行传进来的函数,try/catch是对throw方法错误异常的处理
try{
executor(this.resolve, this.reject)
}catch(e){
this.reject(e)
}
}
initValue(){
this.PromiseResult = null;
this.PromiseState = 'pending';
this.onFulfilledCallbacks = [];// 保存成功的回调
this.onRejectedCallbacks = [];// 保存失败的回调
}
initBind(){
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value){
if(this.PromiseState!== 'pending') return
this.PromiseState = 'fulfilled';
this.PromiseResult = value;
this.onFulfilledCallbacks.forEach((fn)=>fn())
// while(this.onFulfilledCallbacks.length){
// this.onFulfilledCallbacks.shift()(this.PromiseResult)
// }
}
reject(reason){
if(this.PromiseState!== 'pending') return
this.PromiseState = 'rejected'
this.PromiseResult = reason
this.onRejectedCallbacks.forEach((fn)=>fn())
// while(this.onRejectedCallbacks.length){
// this.onRejectedCallbacks.shift()(this.PromiseResult)
// }
}
// 实现then方法
then(onFulfilled, onRejected){
// 分别给onFulfilled和onRejected方法进行初始化
// 然后根据Promise的状态执行对应的方法
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
// 返回Promise对象的操作 .then链式调用的核心
var thenPromise = new MyPromise((resolve, reject)=>{
if(this.PromiseState === 'fulfilled'){
// 核心是判断是否为返回值是否为Promise,是那么要等待其完成,如果不是那么直接onFulfilled方法执行完,resolve直接结束
// 为了防止thenPromise没有生成 要采用异步操作
setTimeout(() => {
try {
let x = onFulfilled(this.PromiseResult)
resolvePromise(x, resolve, reject, thenPromise)
} catch (error) {
reject(error)
}
}, 0);
}else if(this.PromiseState === 'rejected'){
setTimeout(() => {
try {
let x = onRejected(this.PromiseResult)
resolvePromise(x, resolve, reject, thenPromise)
} catch (error) {
reject(error)
}
}, 0);
}else if(this.PromiseState === 'pending'){
// 这里是当Promise的状态为pending,将这些回调函数保存在数组中
// 注意这里回调函数的指向,始终指向的是回调函数的执行者
this.onFulfilledCallbacks.push(()=>{
setTimeout(() => {
try {
let x = onFulfilled(this.PromiseResult)
resolvePromise(x, resolve, reject, thenPromise)
} catch (error) {
reject(error)
}
}, 0);
})// bind绑定保证了this始终指向then方法的调用者,保证值不丢失
this.onRejectedCallbacks.push(()=>{
setTimeout(() => {
try {
let x = onRejected(this.PromiseResult)
resolvePromise(x, resolve, reject, thenPromise)
} catch (error) {
reject(error)
}
}, 0);
})
}
})
// 返回一个包装的Promise
return thenPromise
}
}
function resolvePromise(x, resolve, reject, thenPromise){
if(x === thenPromise){
return reject(new TypeError('不可以返回本身!'))
}
// 防止多次调用
let called;
// x不是Null且x是对象或者函数
if(x !== null && (typeof x === 'object' || typeof x=== 'function')){
try {
// A+ 规定,声明then = x的then方法
let then = x.then
// 如果then是函数,就默认是promise了
if(typeof then === 'function'){
// 就让then执行,第一个参数是this 后面是成功的回调和失败的回调
then.call(x,y=>{
// 失败和成功的回调只能调用一个
if(called) return;
called = true;
// resolve出来的结果是Promise,那么继续解析
resolvePromise(y, resolve, reject, thenPromise)
},err=>{
if(called) return;
called = true;
// 直接执行reject函数
reject(err);
})
}
} catch (error) {
// 也属于失败了
if(called) return;
called = true;
// 去then出错了那么就不要在继续执行了
reject(e)
}
}else{
resolve(x)
}
}
Promise其他重要方法
resolve()&reject()方法
resolve()与reject()方法分别是用来返回一个Promise对象,Promise对象中执行了对应的resolve与reject方法。
// resolve()方法
MyPromise.resolve = function(val){
return new Promise((resolve, reject)=>{
resolve(val);
})
}
// reject()方法
MyPromise.reject = function(val){
return new Promise((resolve, reject)=>{
reject(val);
})
}
// 测试用例如下:
MyPromise.resolve(200).then(data =>{
console.log(data) // 200
})
MyPromise.reject(200).then(data =>{
console.log(data)
},
err=>{
console.log(err) // 200
})
race()方法
race()方法遵循竞速原则,只要其中有一个Promise对象状态发生改变,就优先返回先改变状态的Promise对象。
MyPromise.race = function(promises){
// 遍历数组中的Promise对象,谁先满足条件你谁先执行resolve方法,返回resolve方法这个值
return new MyPromise((resolve,reject)=>{
for(let i = 0;i < promises.length; i++){
if(promises[i] instanceof MyPromise){
promises[i].then(resolve, reject)
}else{
resolve(promises[i]);
}
}
})
}
const p1 = new MyPromise((resolve,reject)=>{
setTimeout(() => {
resolve(300)
}, 3000);
})
const p2 = new MyPromise((resolve,reject)=>{
setTimeout(() => {
resolve(200)
}, 2000);
})
const p3 = new MyPromise((resolve,reject)=>{
setTimeout(() => {
resolve(300)
}, 1000);
})
MyPromise.race([p1,p2,p3]).then((val)=>{
console.log(val) // 返回 300
},(reason)=>{
console.log(reason)
})
all()方法
Promise.all遵循等待原则,等到所有的Promises都满足了条件之后,拿到所有成功的结果。而且拿到的结果是按照满足条件的顺序来展示的
// 接受的是一个数组
MyPromise.all = function(promises){
let arr = [];
let i = 0;//用于计数,保证i === promises.length时,退出循环
function processData(res, index, resolve){
arr[index] = res;
i++;
if(i === promises.length){
resolve(arr);
}
}
return new MyPromise((resolve, reject)=>{
for(let j=0;j<promises.length;j++){
if(promises[j] instanceof Mypromise){
promises[j].then((res)=>{
processData(res,j,resolve)
},(reason)=>{
reject(reason)
})
}else{
processData(promises[j], j);
}
}
})
}
const p1 = new MyPromise((resolve,reject)=>{
setTimeout(() => {
resolve(300)
}, 3000);
})
const p2 = new MyPromise((resolve,reject)=>{
setTimeout(() => {
resolve(200)
}, 2000);
})
const p3 = new MyPromise((resolve,reject)=>{
setTimeout(() => {
resolve(300)
}, 1000);
})
MyPromise.race([p1,p2,p3]).then((val)=>{
console.log(val) // 返回 300
},(reason)=>{
console.log(reason)
})
allSettled()方法
等到Promise数组中所有的Promise全部完成不论是成功还是失败。
MyPromise.allSettled = function(promises){
return new MyPromise((resolve, reject)=>{
let arr = [];
let count = 0;
function processData(status,res,index){
arr[index] = {status,value:res};
count++;
if(count === promises.length){
resolve(arr);
}
}
promises.forEach((item,index)=>{
if(item instanceof MyPromise){
item.then(res=>{
processData('fulfilled',res,index);
},err=>{
processData('rejected',err,index);
})
}else{
processData('fulfilled',item,index);
}
})
})
}
MyPromise.allSettled([p1,p2,p3]).then((arr)=>{
console.log(arr)
},(reason)=>{
console.log(new Error(reason))
})
any()方法
当所有的Promise对象都报错了,该方法会返回报错的原因
MyPromise.any = function(promises){
return new MyPromise((resolve,reject)=>{
let count = 0;
promises.forEach((item)=>{
if(item instanceof MyPromise){
item.then((res)=>{
resolve(res)
},(err)=>{
count++;
if(count === promises.length){
reject('Promise都有问题')
}
})
}else{
resolve(item)
}
})
})
}
const p4 = new MyPromise((resolve,reject)=>{
setTimeout(() => {
reject(300)
}, 200);
})
const p5 = new MyPromise((resolve,reject)=>{
setTimeout(() => {
reject(400)
}, 300);
})
const p6 = new MyPromise((resolve,reject)=>{
setTimeout(() => {
reject(500)
}, 400);
})
MyPromise.any([p4,p5,p6]).then(()=>{},(reason)=>{
console.log(reason);
})
总结
经过这次 Promise原理的学习,让我深入理解了promise的then方法的链式调用,异步操作的使用与处理,数据类型的判断。对我的JS基础方面解析进一步的巩固。