Promise手写源码以及api详解-----学了就会学完就忘系列
作者:互联网
一、 Promise
核心逻辑
promise
就是一个类,在执行这个类的时候 需要传递一个执行器(也就是里面这个回调函数,箭头函数),执行器会立即执行( 当我们new Promise
的时候 回调会立即调用)- 回调函数有两个参数
resolve
,reject
,这两个参数其实就是两个函数 Promise
中有三种状态,分别为
成功fulfilled
失败rejected
等待pending
- 状态特点:
等待变成成功pending
->fulfilled
等待变成失败pending
->rejected
一旦状态确定成功/失败,就不可以改变 - resolve 和reject 函数是用来更改状态的
resolve
->fulfilled
reject
->rejected
- 我们创建完
promise
对象以后,我们可以创建一个变量去接收
promise
下面可以调用then
方法来传递两个回调函数,成功与失败,也就说当我们调用then在这里插入代码片
方法的时候我们要去判断promise
的返回状态,如果成功则调用的第一个(成功的回调函数),失败则第二个(失败的回调函数) then
方法内部做的事情就是判断状态,如果成功则调用的第一个(成功的回调函数),失败则第二个(失败的回调函数),then
成功回调(successCallback
)有一个参数,表示成功之后返回的值value
,then
失败回调(failCallback
)后有一个返回值reason
失败原因
二、 Promise
基础代码
- 通过
class
关键字创建一个类MyPromise
- 我们需要使用构造函数
constructor
来接受这个构造函数,executor
代表执行器(执行器是立即执行的,执行器就代表回调函数)
/*
1、通过`class`关键字创建一个类 MyPromise
2、我们需要使用构造函数constructor 来接受这个构造函数,executor代表执行器(执行器是立即执行的,执行器就代表回调函数)
*/
const PENGDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
// 当前我们是在一个类里面,我们要通过this去访问
executor(this.resolve, this.reject)
}
/*
1、 为什么定义成箭头函数?
将来在调用resolve 和reject的时候是直接调用的,如果是普通函数,函数里面的指向-window/reject,如果是箭头函数,会改变this指向,是内部指向类的实例对象,指向promise 对象
2、 resolve 和reject 就是用来更改状态的。
3、我们要求定义状态,定义成常量,好处:复用,便于开发。
*/
// 初始状态PENGDING
status = PENGDING
/*
8、设置默认成功之后的值,失败之后的原因
*/
value = undefined
reason = undefined
/*
9、成功回调,失败回调诉默认值
*/
successCallback = undefined
failCallback = undefined
resolve = value => {
/*
4、如果状态不是等待,阻止程序向下执行,否则将状态更改为成功
*/
if (this.status !== PENGDING) return;
this.status = FULFILLED
/*
7、保存成功之后的值 ,then方法中调用失败回调(successCallback)的时候可以传递当前保存的this.value
*/
this.value = value
/*
10、判断成功回调是否存在,如果存在则调用,
*/
this.successCallback && this.successCallback(this.value)
}
reject = reason => {
/*
5、如果状态不是等待,阻止程序向下执行,否则将状态更改为失败
*/
if (this.status !== PENGDING) return;
this.status = REJECTED
/*
7、保存失败之后的原因,then方法中调用失败回调(failCallback)的时候可以传递当前保存的this.reason
*/
this.reason = reason
/*
10、判断失败回调是否存在,如果存在则调用,
*/
this.failCallback && this.failCallback(this.reason)
}
// 6、then方法 判断状态
then(successCallback, failCallback) {
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
// 9、这里我们处理的就是当我们处于pending 状态的时候也就是出现异步情况的时候的.首先我们把成功回调和失败回调存储起来,等异步代码执行完毕之后才执行这个成功/失败回调 -然后我们就要找成功resolve-失败reject的回调
this.successCallback = successCallback
this.failCallback = failCallback
}
}
}
//调用
let promise = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失败')
})
promise.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
三、当Promise 类中加入异步逻辑
- 假设我们调用的时候是下面这种,出现异步的情况 延时返回-
let promise = new MyPromise((resolve, reject) => {
// 假设当出现异步情况
setTimeout(()=>{
resolve('成功')
},2000)
// reject('失败')
})
promise.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
1、 出现异步情况的实现办法
1 当出现`setTimeOut` 或者其他异步的情况的时候,导致代码没有直接调用`resolve`/`reject` 函数,此时 状态是`pengding` 所以按照代码运行顺序会直接进入到`then`方法,`then`方法会先执行,但是此时因为状态一直是`pengding` 我们并不知道是调用`successCallBack` 还是failCallBack,
2 ------------所以我们先将其存储(代码注释①)起来,当异步代码执行完毕之后我们才会执行resolve
/reject
,然后去判断成功/失败回调是否存在,若存在则调用它(代码注释②)。
const PENGDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENGDING
value = undefined
reason = undefined
successCallback = undefined
failCallback = undefined
resolve = value => {
if (this.status !== PENGDING) return;
this.status = FULFILLED
this.value = value
/* 注释② */
this.successCallback && this.successCallback(this.value)
}
reject = reason => {
if (this.status !== PENGDING) return;
this.status = REJECTED
this.reason = reason
/* 注释 ② */
this.failCallback && this.failCallback(this.reason)
}
then(successCallback, failCallback) {
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
/* 注释①
- 这里处理的就是当处于pending ,也就是出现异步情况的时候的:
- 首先我们把成功回调和失败回调存储起来,
- 等异步代码执行完毕之后才执行这个成功/失败回调
- 然后我们就要找成功resolve-失败reject的回调
*/
this.successCallback = successCallback
this.failCallback = failCallback
}
}
}
四、实现 then 方法多次调用添加多个处理函数
当then
方法被多次调用的时候,每一个then
方法中传递的回调函数都是要被执行的,这个时候我们要如何处理呢?
- 假设我们调用的时候是下面这种,多次调用
then
方法,
let promise = new MyPromise((resolve, reject) => {
// 假设当出现异步情况
setTimeout(()=>{
resolve('成功')
},2000)
// reject('失败')
})
promise.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
promise.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
promise.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
1、实现过程及分析
两种情况,一种是同步的情况 ,一种是异步的情况
下面我们就详细说明:每一个then
方法的回调函数都应该存储起来,当状态变为成功或者失败的时候,在依次调用里面的回调函数 上一个实例中出现异步的情况我们每次只能存储一个回调函数,所以我们要基于上面的情况再次进行处理
- 找到成功与失败回调设置默认值 由
undefined
改为[]空数组
,便于我们去存储多个回调函数 - 我们找到存储回调的地方将赋值改成 将我们的每个回调 push到我们的相应存储数组里面
- 当这个状态是成功还是失败的时候,这个时候我们应该去调用数组中的函数,这个函数如何调用呢?
循环数组(while
) 并调用
const PENGDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENGDING
value = undefined
reason = undefined
// successCallback = undefined
// failCallback = undefined
successCallback = []
failCallback = []
resolve = value => {
if (this.status !== PENGDING) return;
this.status = FULFILLED
this.value = value
// this.successCallback && this.successCallback(this.value)
// shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
while(this.successCallback.length>0) this.successCallback.shift()(this.value)
}
reject = reason => {
if (this.status !== PENGDING) return;
this.status = REJECTED
this.reason = reason
// this.failCallback && this.failCallback(this.reason)
// shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
while(this.failCallback.length>0) 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 = successCallback
// this.failCallback = failCallback
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
}
}
打印结果
五、实现 then 方法的链式调用(一)-普通值
Promise
的then
方法是可以链式调用的,后面then
方法的回调函数拿到的值实质上是上一个then
方法的回调函数的返回值
下面我们来演示一下链式调用返回的普通值的时候:
let promise = new MyPromise((resolve, reject) => {
// 当出现异步情况
// setTimeout(()=>{
// resolve('成功')
// },2000)
resolve('成功')
// reject('失败')
})
promise.then(value => {
console.log(value)
return 100;
}).then(value => {
console.log(value) //100
})
###1、 实现方法及分析
下面我们通过两个步骤来实现
- 如何实现
then
方法的链式调用
先我们要清楚,then
方法是Promise
下面的,如果我们想要实现then方法下面的链式调用,那我们的每一个then
方法返回的都应该是一个Promise
对象,这样才可以链式调用then
- 如何把上一个函数的返回值,传递给下一个
then
方法的回调函数
首先拿到回调返回值,然后 把这个返回值传递给下一个then
回调函数,实际上下一个then 就是我们返回这个promise2
下面的then
, 那也就是说我们调用promise
里面的resolve
方法就好了,当我们调用完resolve
之后,就实现了
const PENGDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {executor(this.resolve, this.reject)}
status = PENGDING
value = undefined
reason = undefined
successCallback = []
failCallback = []
resolve = value => {
if (this.status !== PENGDING) return;
this.status = FULFILLED
this.value = value
while(this.successCallback.length>0)
this.successCallback.shift()(this.value)
}
reject = reason => {
if (this.status !== PENGDING) return;
this.status = REJECTED
this.reason = reason
while(this.failCallback.length>0)
this.failCallback.shift()(this.reason)
}
then(successCallback, failCallback) {
/*
1. 既然我们要返回一个Promise 对象,那么我们创建一个Promise 对象,
2. 然后我们通过return 关键字 返回 promise2
3. 传递一个执行器,(立即执行)使原来的代码保持原有的功能
4. ---这样我们就实现了步骤一
*/
let promise2 = new MyPromise((resolve,reject)=>{
if (this.status === FULFILLED) {
/* 实现步骤二
①. 首先拿到函数的返回值(成功/失败)返回的promise2
②. 把返回值传递给下一个then回调函数,调用promise2里的resolve方法
*/
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 promise2
}
}
- 返回实现结果
六、实现 then 方法的链式调用(一)-Promise对象
当我们以promise
对象为返回值时
let promise = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失败')
})
// 先定义一个promise 对象
function otherPromise(params) {
return new Promise((resolve,reject)=>{
resolve('other')
})
}
//
promise.then(value => {
console.log(value)
return otherPromise();
}).then(value => {
console.log(value)
})
1、当返回值是promise对象时实现方法
接下来我们要去判断 x 是普通值还是promise
对象
如果是普通值,直接resolve
如果是promise
对象 查看promise
对象返回的结果
在根据promise
对象返回的结果(Promise
对象的状态),
若成功-----> 调用resolve
将成功的值传递给下一个
若失败-----> 调用reject
将失败的结果传递给下一个
const PENGDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENGDING
value = undefined
reason = undefined
successCallback = []
failCallback = []
resolve = value => {
if (this.status !== PENGDING) return;
this.status = FULFILLED
this.value = value
while (this.successCallback.length > 0)
this.successCallback.shift()(this.value)
}
reject = reason => {
if (this.status !== PENGDING) return;
this.status = REJECTED
this.reason = reason
while (this.failCallback.length > 0)
this.failCallback.shift()(this.reason)
}
then(successCallback, failCallback) {
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
let x = successCallback(this.value)
resolvePromise(x, resolve, reject)
// resolve(x)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
});
// 2然后我们通过return 关键字 返回promise2
return promise2
}
}
// 判断x是普通值还是Promise对象
function resolvePromise(x, resolve, reject) {
// 我们只需要判断x 是不是MyPromise 的这个实例
if (x instanceof MyPromise) {
// x.then(value => { resolve(value)}, reason => { reject(reason)})
//简化代码
x.then(resolve,reject)
} else {
resolve(x)
}
}
七、then 方法链式调用识别 Promise 对象自返回
-
在then 方法中是可以返回
promise
对象的,但是有一种个例情况在then方法中 不能返回当前这个方法他所返回的这个promise 对象, 如果说你所返回的then方法返回的那个promise对象, 那这个时候就发生了promise对象的循环调用,程序是不允许的,会报错!!
1、html模拟演示个例
我们新建一个index.html
文件,输入以下代码,
模拟演示一下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var a = new Promise(function (resolve, reject) {
resolve('100')
})
var p1 = a.then(function (value) {
console.log(value)
return p1
})
</script>
</body>
</html>
查看当前系统结果 会报错
2、在当前promise中判断个例情况
首先我们调用的时候
let promise = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失败')
})
let p1=promise.then(value => {
console.log(value)
return p1;
})
p1.then(value => {
console.log(value)
},reason=>{
console.log(reason.message)
})
3、Promise 对象自返回解决方法
1、 找到then方法
2、返回的promise对象就是promise2 ,
成功的回调返回的promise对象就是x
3、我们只需要判断promise对象和x是否相等,
如果相等则就说明是自己返回了自己,
出现问题了,我们就要执行reject,
4、在resolvePromise中处理
const PENGDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) { executor(this.resolve, this.reject)}
status = PENGDING
value = undefined
reason = undefined
successCallback = []
failCallback = []
resolve = value => {
if (this.status !== PENGDING) return;
this.status = FULFILLED
this.value = value
while (this.successCallback.length > 0) this.successCallback.shift()(this.value)
}
reject = reason => {
if (this.status !== PENGDING) return;
this.status = REJECTED
this.reason = reason
while (this.failCallback.length > 0) this.failCallback.shift()(this.reason)
}
then(successCallback, failCallback) {
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
// 异步代码原因:是此过程执行完在执行词代码 是promise2 可以传值不会报错
setTimeout(()=>{
let x = successCallback(this.value)
resolvePromise(promise2,x, resolve, reject)
},0)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
});
return promise2
}
}
function resolvePromise(promise2,x, resolve, reject) {
// 如果相等,就说明自己反悔了自己,捕捉到了问题,提示错误
if(promise2===x){
// 使用return 是出现问题程序报错后阻止代码向下执行
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (x instanceof MyPromise) {
x.then(resolve,reject)
} else {
resolve(x)
}
}
执行结果:
我们就可以直接查看我们捕捉的错误,就可以啦~
八、捕获错误及then链式调用其他状态代码
上述的所有我们没有对任何错误情况进行捕捉,下面我们对一些错误进行捕捉
1、 执行器错误
当执行器中的代码在执行的过程当中发生错误的时候,
我们就要把执行的状态变成是失败的状态
let promise = new MyPromise((resolve, reject) => {
// 我们抛出错误演示一下
throw new Error('执行器错误')
resolve('成功')
// reject('失败')
})
promise.then(value => {
console.log(value)
},reason=>{
console.log(reason,‘=’)
})
捕捉异常处理,我们只需要在上述代码中在执行器处
通过try ,catch来捕获异常,调用reject方法打印错误
constructor(executor) {
// 执行器中捕捉错误
try{
executor(this.resolve, this.reject)
} catch (e){
// 如果捕捉到了错误 我们把错误原因e传递过去
this.reject(e)
}
// executor(this.resolve, this.reject)
}
结果显示
2、 then方法里面的回调函数错误
then方法里面的回调函数,若在执行过程中报错了,
下面我们演示一下回调函数执行中异常并抛出错误。
let promise = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失败')
})
promise.then(value => {
console.log(value)
// 我们抛出错误演示一下
throw new Error('回调函数错误')
},reason=>{
console.log(reason)
}).then(value => {
console.log(value)
},reason=>{
console.log(reason.message)
})
捕捉到这个错误,并且这个错误要在下一个then方法的错误回调reject中捕捉到,并返回
then(successCallback, failCallback) {
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(()=>{
// then方法里面的回调函数中捕捉错误
try{
let x = successCallback(this.value)
resolvePromise(promise2,x, resolve, reject)
}catch(e){
reject(e)
}
},0)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
});
return promise2
}
实现结果
3、链式调用捕捉错误结果返回
我们之前处理链式调用 只处理了成功的状态 并没有处理失败的状态,
那么当失败的时候或者当代码是异步的时候都没有处理
模拟演示实现错误调用
let promise = new MyPromise((resolve, reject) => {
// resolve('成功')
reject('失败')
})
promise.then(value => {
// console.log(value)
},reason=>{
console.log(reason)
return "失败"
}).then(value => {
console.log(value,'捕捉错误回调')
})
捕捉代码处理,在then方法中
then(successCallback, failCallback) {
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(()=>{
// then方法里面的回调函数中捕捉错误
try{
let x = successCallback(this.value)
resolvePromise(promise2,x, resolve, reject)
}catch(e){
reject(e)
}
},0)
} else if (this.status === REJECTED) {
// then方法里面的失败函数中捕捉错误
setTimeout(()=>{
try{
let x = failCallback(this.reason)
resolvePromise(promise2,x, resolve, reject)
}catch(e){
reject(e)
}
},0)
} else {
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
});
return promise2
}
执行结果
4、当代码是异步的情况捕捉错误
- 当我们异步调用的时候
let promise = new MyPromise((resolve, reject) => {
setTimeout(()=>{
resolve('成功....')
},2000)
// resolve('成功')
// reject('失败')
})
promise.then(value => {
console.log(value)
return 'aaaa'
},reason=>{
console.log(reason)
}).then(value => {
console.log(value,'捕捉错误回调')
})
-
捕捉处理
之前我们链式调用的时候是push到一个数组里, 那这里呢我们需要push一个函数进去, 在这个函数里面进行函数回调 成功/失败
const PENGDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
try{
executor(this.resolve, this.reject)
} catch (e){
this.reject(e)
}
}
status = PENGDING
value = undefined
reason = undefined
successCallback = []
failCallback = []
resolve = value => {
if (this.status !== PENGDING) return;
this.status = FULFILLED
this.value = value
// 当处理异步 捕捉错误的时候这个手已经不需要传值了,
while (this.successCallback.length > 0) this.successCallback.shift()()
}
reject = reason => {
if (this.status !== PENGDING) return;
this.status = REJECTED
this.reason = reason
// 当处理异步 捕捉错误的时候这个手已经不需要传值了,
while (this.failCallback.length > 0) this.failCallback.shift()()
}
then(successCallback, failCallback) {
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(()=>{
// then方法里面的回调函数中捕捉错误
try{
let x = successCallback(this.value)
resolvePromise(promise2,x, resolve, reject)
}catch(e){
reject(e)
}
},0)
} else if (this.status === REJECTED) {
// failCallback(this.reason)
setTimeout(()=>{
// then方法里面的回调函数中捕捉错误
try{
let x = failCallback(this.reason)
resolvePromise(promise2,x, resolve, reject)
}catch(e){
reject(e)
}
},0)
} else {
this.successCallback.push(()=>{
setTimeout(()=>{
// then方法里面的回调函数中捕捉错误
try{
let x = successCallback(this.value)
resolvePromise(promise2,x, resolve, reject)
}catch(e){
reject(e)
}
},0)
})
this.failCallback.push(()=>{
setTimeout(()=>{
// then方法里面的回调函数中捕捉错误
try{
let x = failCallback(this.reason)
resolvePromise(promise2,x, resolve, reject)
}catch(e){
reject(e)
}
},0)
})
}
});
return promise2
}
}
实现结果 2s后打印
九、链式调用时将then方法的参数变成可选参数
处理方法
// 在then 方法中添加
successCallback = successCallback ? successCallback : value => value;
failCallback = failCallback ? failCallback : reason => { throw reason }
//成功调用
let promise = new MyPromise((resolve, reject) => {
resolve('成功....')
})
promise.then().then().then(value=>console.log(value))
// 失败调用
let promise = new MyPromise((resolve, reject) => {
reject('失败')
})
promise.then().then().then(value=>console.log(value),reason=>{console.log(reason)})
以上内容均为个人学习笔记,不存在任何其他或者商业行为 ,如有侵权或者其他,必删除。请私聊或者评论告知。
标签:resolve,failCallback,会学,successCallback,value,reason,api,reject,源码 来源: https://blog.csdn.net/weixin_43405717/article/details/117709048