1、promise的几个关键问题
如何改变promise的状态?
resolve(value):如果当前是pending就会变为resolved
reject(value):如果当前是pending就会变为rejected
抛出异常:如果当前是pending就会变为rejected
const p = new Promise((resolve, reject) => {
//1.resolve
// resolve("success");//pending => fulfilled (resolved)
//2.reject
// reject("error");//pending => rejected
//3.抛出异常
throw "异常";//pending => rejected
})
一个promise指定多个成功/失败回调函数,都会调用吗?
当peomise改变为对应状态时都会调用
改变promise状态和指定回调函数谁先谁后?
都有可能,正常情况下是指定回调在改变状态,因为大部分都是放的异步代码,所以请求发送还没完成,就已经执行then()指定回调了,而此时promise的状态还是pening,但也可以改状态再指定回调,注意指定then()回调并不是调用then()中的回调函数
const p = new Promise((resolve, reject) => {
//异步代码
setTimeout(() => {
resolve("success");
});
})
p.then(res => {
console.log(res);
});
如何先改变状态再指定回调?
在执行器中直接调用resolve()/reject()
延迟更长时间才调用then()
什么时候才能得到数据
如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据
promise.then()返回的新的promise的结果状态是由什么决定的
简单表达:由then()指定的回调函数执行的结果决定的
详细表达:
如果抛出异常,新promise变为rejected,reason为抛出的异常
如果返回的是非promise的任意值,新pormise变为resolved,value为返回的值
如果返回的是另一个新promise,此promise的结果就会成为新的promise的结果
promise如何串联多个操作任务?
promise的then()返回一个新的promise,可以看成then()的链式调用
通过then()的链式调用串联多个同步/异步任务
promise异常穿透
当使用promise的then链式调用时,可以在最后指定失败的回调
前面任何操作出了异常,都会传到最后失败的回调中处理
中断promise链?
当使用promise的then链式调用时,在中间中断,不在调用后面的回调函数
唯一办法:在回调函数中返回一个pending状态的promise对象
2、Promise的自定义封装
2.1、定义整体结构
首先来看一下原生Promise的基本结构
let p = new Promise((resolve, reject) => {
//resolve();//成功回调
//reject();//失败回调
})
p.then(res => {
}, err => {
})
所以我们根据这个就先定义一个Promise构造函数
function MyPromise(executor) {
}
/* MyPromise.prototype */
MyPromise.prototype = {
constructor: MyPromise,
/*添加 then() */
then(onResolved, onRejected) {
}
}
then()是通过实例对象调用的,所以就把then定义在原型上面
2.2、resolve与reject结构搭建
首先从 new Promise((resolve, reject) => {}) 这里作为一个突破点。
这个执行器函数在内部是同步调用的,就说明只要new MyPromise就会立即执行执行器函数executor()
执行器函数本身也有两个参数resolve和 reject,通过原生就知道这两个参数都是用来调用成功/失败的函数,接下来可以申明resolve和reject
resolve和reject都有实参
function MyPromise(executor) {
//resolve
const resolve = function (data) {
}
//reject
const reject = function (data) {
}
//同步调用 executor()
executor(resolve, reject);
}
注意:由于resolve和reject使用const/let定义的,没有变量提升,所以 executor(resolve, reject)要写在后面
2.2.1、resolve与reject代码实现
resolve和reject只要被调用都可以修改promise的状态(promiseState)
可以修改promise的结果值(promiseResult)
/* 定义promise的三个状态 */
const PENDING = 'pending'; //待定
const FULFILLED = 'fulfilled'; //成功
const REJECTED = 'rejected'; //失败
function MyPromise(executor) {
//添加属性 起始值
this.PromiseState = PENDING;
this.PromiseResult = undefined;
//保存实例对象的值
const _this = this;
//resolve
const resolve = function (data) {
//1.修改对象的成功状态 (PromiseState);
_this.PromiseState = FULFILLED;
//2.设置对象结果值 (PromiseResult);
_this.PromiseResult = data;
}
//reject
const reject = function (data) {
//1.修改对象的失败状态 (PromiseState);
_this.PromiseState = REJECTED;
//2.设置对象结果值 (PromiseResult);
_this.PromiseResult = data;
}
//同步调用 executor()
executor(resolve, reject);
}
由于promise有固定的三个状态,这里就设置成了常量(当然也可以不声明,看个人习惯)
不过要注意的是reject和reject都是用的函数声明,所以函数在外面调用的时候这里面的this会指向window/undefined,就要在外面申明一个变量保存一下这个值,当然如果改成箭头函数就不用考虑了,它会指向当前函数声明的作用域,不过这里我随大众,在构造函数里面就都用函数声明了
2.2.2、resolve与reject代码实现(throw抛出异常改变状态)
改变promise状态的三种方式:
resolve(成功)
reject(失败)
抛出异常 (失败)
如果不做抛出异常改变状态的处理,就直接报错,整个代码都不执行了
let p = new MyPromise((resolve, reject) => {
throw "err";
})
console.log(p);
再来看看原生的promise,就会把promise的状态改为失败
这里就可以利用try ...catch()来处理异常,这里是executor()函数执行器调用的,
所以函数执行器在哪调用,try ...catch()就在哪加
try {
//同步调用 executor()
executor(resolve, reject);
} catch (e) {
//抛出异常就调用reject修改状态为失败
//抛出的数据e就promise失败的结果值
reject(e)
}
2.2.3、Promise对象的状态只能修改一次
let p = new MyPromise((resolve, reject) => {
resolve("OK");
reject("Error");
})
很明显这是先修改为成功的之后,又修改为失败的了,但是promise的规则就是只允许修改一次,所以继续从resolve和reject下手
如果状态改过了,就不用去改
如果状态没改过,就去更改
//resolve
const resolve = function (data) {
//判断状态
//通过PromiseState是否为pending判断
if (_this.PromiseState !== PENDING) return;
//1.修改对象的成功状态 (PromiseState);
_this.PromiseState = FULFILLED;
//2.设置对象结果值 (PromiseResult);
_this.PromiseResult = data;
}
//reject
const reject = function (data) {
//判断状态
if (_this.PromiseState !== PENDING) return;
//1.修改对象的失败状态 (PromiseState);
_this.PromiseState = REJECTED;
//2.设置对象结果值 (PromiseResult);
_this.PromiseResult = data;
}
2.3、then方法
2.3.1、then方法指定执行回调
then有两个参数,分别对应成功和失败状态的执行回调
成功就执行then的第一个参数
失败就执行then的第二个参数
then的两个参数函数都有一个形参,而形参就是当前promise的结果值PromiseResult
MyPromise.prototype = {
constructor: MyPromise,
/*添加 then() */
then(onResolved, onRejected) {
//调用回调函数 通过当前promise实例对象的PromiseState判断
if (this.PromiseState === FULFILLED) {
onResolved(this.PromiseResult);
} else if (this.PromiseState === REJECTED) {
onRejected(this.PromiseResult);
}
}
}
2.3.2、异步任务执行回调
如果说在实例Promise的时候里面是异步代码会怎么样?
在真正开发中,一般都是异步请求,文件io等等,这里先用定时器模拟一下异步代码,定时器本身也是个异步
首先看一下原生的
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("OK");
}, 1000);
})
p.then(res => {
console.log(res);
}, err => {
console.log(err);
})
console.log(p);
自己的:
状态和结果确实改了,但是没有执行then方法,也就没有打印结果,这是什么原因?
首先我们要知道的是,现在这个then()他是属于同步代码,js一运行代码从上往下走,then也就立马执行,而resolve("OK")需要等到1s之后才调用,而此时promise对象的状态还没有修改,then又执行了,所以也就没有进到then的if语句里面
改变状态之后再去调用then的参数函数
只有resolve/reject方法才改变状态
在resolve/reject方法里面去调用then的参数函数
通过这个几个问题继续扩展
在then里面去判断pending的状态
声明一个属性先保存一下当前的这个回调函数,暂时不让他执行
等异步结束之后在resolve/reject方法里面去执行保存之后的回调函数
function MyPromise(executor) {
//添加属性 起始值
this.PromiseState = PENDING;
this.PromiseResult = undefined;
//保存回调函数
this.callBack = {};
//保存实例对象的值
const _this = this;
//resolve
const resolve = function (data) {
//判断状态
if (_this.PromiseState !== PENDING) return;
//1.修改对象的成功状态 (PromiseState);
_this.PromiseState = FULFILLED;
//2.设置对象结果值 (PromiseResult);
_this.PromiseResult = data;
//调用成功的回调
//如果onResolved存在
if (_this.callBack.onResolved) {
_this.callBack.onResolved(data);
}
}
//reject
const reject = function (data) {
//判断状态
if (_this.PromiseState !== PENDING) return;
//1.修改对象的失败状态 (PromiseState);
_this.PromiseState = REJECTED;
//2.设置对象结果值 (PromiseResult);
_this.PromiseResult = data;
//调用失败的回调
if (_this.callBack.onResolved) {
_this.callBack.onRejected(data);
}
}
try {
//同步调用 executor()
executor(resolve, reject);
} catch (e) {
//抛出异常就调用reject修改状态为失败
//抛出的数据 e 就promise失败的结果值
reject(e)
}
}
MyPromise.prototype = {
constructor: MyPromise,
/*添加 then() */
then(onResolved, onRejected) {
//调用回调函数 通过当前promise实例对象的PromiseState判断
if (this.PromiseState === FULFILLED) {
onResolved(this.PromiseResult);
} else if (this.PromiseState === REJECTED) {
onRejected(this.PromiseResult);
} else if (this.PromiseState === PENDING) {
//保存回调函数
this.callBack = {
onResolved: onResolved,
onRejected: onRejected
}
}
}
}
2.3.3、链式调用
在Promise中,只要状态发生改变,下面的方法都会执行,也就是链式调用,而我们自己写的暂时不行,因为每一次调用then都会覆盖掉上一次调用的then的结果和状态,所以不能这样保存。多个保存就用数组
属性:this.callBack = {} 换成 this.callBacks = []
// this.callBack = {
// onResolved: onResolved,
// onRejected: onRejected
// }
//改成
this.callBacks.push({
onResolved: onResolved,
onRejected: onRejected
})
构造函数里面也改一下,数组遍历调用
const resolve = function (data) {
//判断状态
if (_this.PromiseState !== PENDING) return;
//1.修改对象的成功状态 (PromiseState);
_this.PromiseState = FULFILLED;
//2.设置对象结果值 (PromiseResult);
_this.PromiseResult = data;
//调用成功的回调
_this.callBacks.forEach(item => {
item.onResolved(data);
})
}
//reject
const reject = function (data) {
//判断状态
if (_this.PromiseState !== PENDING) return;
//1.修改对象的失败状态 (PromiseState);
_this.PromiseState = REJECTED;
//2.设置对象结果值 (PromiseResult);
_this.PromiseResult = data;
//调用失败的回调
_this.callBacks.forEach(item => {
item.onRejected(data);
})
}
2.3.4、同步任务下then()返回的结果
then返回的是一个promise对象
then()的形参函数没有返回值,那then返回的是一个结果为undefined并且状态为成功的promise对象
then()的形参函数的返回值是非promise对象,那then返回结果是结果为这个形参的返回值并且状态为成功的promise对象
then()的形参函数的返回值是promise对象,那这个promise对象的返回结果就是这个then的返回结果
then()的形参函数抛出异常,那then返回的是一个结果为抛出异常的值并且状态为失败的promise对象
根据上面then的返回值特点,接下来在then中就可以用一个变量来接受这个回调函数的一个返回结果来判断
then(onResolved, onRejected) {
//调用回调函数 通过当前promise实例对象的PromiseState判断
return new MyPromise((resolve, reject) => {
if (this.PromiseState === FULFILLED) {
//抛出异常,没有异常
try {
//获取回调函数的执行结果
let result = onResolved(this.PromiseResult);
if (result instanceof MyPromise) {
//如果是 MyPromise对象
//这个MyPromise对象是成功就返回成功,失败就返回失败
result.then(res => {
resolve(res);
}, err => {
reject(err);
})
} else {
//结果的对象状态为成功
resolve(result);
}
} catch (e) {//捕获异常
reject(e);
}
} else if (this.PromiseState === REJECTED) {
onRejected(this.PromiseResult);
} else if (this.PromiseState === PENDING) {
//保存回调函数
this.callBacks.push({
onResolved: onResolved,
onRejected: onRejected
})
}
})
}
注意:这是同步状态下,如果加了定时器,效果不会出来,不过后面会解决
2.3.5、异步任务下then()返回的结果
如果我们加个定时器,异步代码会怎样
let p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("OK");
}, 1000);
})
const result = p.then(res => {
console.log(res);
}, err => {
console.log(err);
})
console.log(result);
打印出来发现promise的状态和值都没有发什么变化,这是什么原因
所以返回的这个promise对象是没有发生状态改变的promise对象,那怎么解决?
then(onResolved, onRejected) {
const _this = this;
//调用回调函数 通过当前promise实例对象的PromiseState判断
return new MyPromise((resolve, reject) => {
if (this.PromiseState === FULFILLED) {
try {
//获取回调函数的执行结果
let result = onResolved(this.PromiseResult);
if (result instanceof MyPromise) {
//如果是 MyPromise对象
result.then(res => {
resolve(res);
}, err => {
reject(err);
})
} else {
//结果的对象状态为成功
resolve(result);
}
} catch (e) {
reject(e);
}
} else if (this.PromiseState === REJECTED) {
onRejected(this.PromiseResult);
} else if (this.PromiseState === PENDING) {
//保存回调函数
this.callBacks.push({
onResolved: function () {
//执行成功的回调函数
//成功的结果就是当前promise实例对象的PromiseResult
//获取回调函数的执行结果
let result = onResolved(_this.PromiseResult);
//根据返回的结果判断这个promise的返回状态和值
if (result instanceof MyPromise) {
//如果是 MyPromise对象
result.then(res => {
resolve(res);
}, err => {
reject(err);
})
} else {
//结果的对象状态为成功
resolve(result);
}
},
onRejected: function () {
//老规矩
let result = onRejected(_this.PromiseResult);
//根据返回的结果判断这个promise的返回状态和值
if (result instanceof MyPromise) {
//如果是 MyPromise对象
result.then(res => {
resolve(res);
}, err => {
reject(err);
})
} else {
//结果的对象状态为成功
resolve(result);
}
}
})
}
})
}
解释一下:
_this.callBacks.forEach(item => {
item.onRejected();
})
就拿下面这串代码来说,我知道到这里肯定会不懂,这个数组在上面的resolve/reject中就遍历调用了
//保存回调函数
this.callBacks.push({
onResolved: function () {
//执行成功的回调函数
//成功的结果就是当前promise实例对象的PromiseResult
//获取回调函数的执行结果
let result = onResolved(_this.PromiseResult);
//根据返回的结果判断这个promise的返回状态和值
if (result instanceof MyPromise) {
//如果是 MyPromise对象
result.then(res => {
resolve(res);
}, err => {
reject(err);
})
} else {
//结果的对象状态为成功
resolve(result);
}
},
onRejected: function () {
//老规矩
let result = onRejected(_this.PromiseResult);
//根据返回的结果判断这个promise的返回状态和值
if (result instanceof MyPromise) {
//如果是 MyPromise对象
result.then(res => {
resolve(res);
}, err => {
reject(err);
})
} else {
//结果的对象状态为成功
resolve(result);
}
}
})
而只要异步代码一执行完毕就会改变状态,只要一改变状态就会执行resolve/reject,只要一执行resolve/reject,就会遍历数组执行数组里面的函数。
我们先来理清一下,onResolved是then()中的第一个参数作为回调函数,只要promise成功就会执行这个函数,之后就获取这个回调函数的执行结果,onResolved(_this.PromiseResult) 就是当前调用then的promise,而他里面的参数就是这个promise的结果值(这是在另一个函数里面所以要在外面通过const _this = this 保存一下this值,不能直接获取this),然后又是老规矩,如果不是MyPromise对象,结果的对象状态就是成功,如果是MyPromise对象,那这个MyPromise对象成功,我就resolve,如果失败,我就reject,其实这里就是把传进来的MyPromise对象的结果返回给then()中新MyPromise,保持一致
还有一点要注意,在控制台上查看打印结果的时候,一定要等定时器执行完改变状态之后再点开Promise对象,不然的话看到的结果还是定时器之前的结果,因为console.log()是个同步代码
当然抛出异常也一样会返回一个失败的失败的promise对象,所以这里也照样加一个try...catch(e)...
this.callBacks.push({
onResolved: function () {
try {
//执行成功的回调函数
//成功的结果就是当前promise实例对象的PromiseResult
//获取回调函数的执行结果
let result = onResolved(_this.PromiseResult);
//根据返回的结果判断这个promise的返回状态和值
if (result instanceof MyPromise) {
//如果是 MyPromise对象
result.then(res => {
resolve(res);
}, err => {
reject(err);
})
} else {
//结果的对象状态为成功
resolve(result);
}
} catch (e) {
reject(e);
}
},
onRejected: function () {
//老规矩
try {
let result = onRejected(_this.PromiseResult);
//根据返回的结果判断这个promise的返回状态和值
if (result instanceof MyPromise) {
//如果是 MyPromise对象
result.then(res => {
resolve(res);
}, err => {
reject(err);
})
} else {
//结果的对象状态为成功
resolve(result);
}
} catch (e) {
reject(e);
}
}
})
2.3.6、then的最后优化
到这里其实还有一个小问题。就是还没有对失败的状态做一个添加
可结果,promise的状态还是pending
造成这样的原因是,在then()中,并没有对失败的结果设置,所以就直接返回初始的promise
所以这里又是老规矩
try {
let result = onRejected(this.PromiseResult);
if (result instanceof MyPromise) {
//如果是 MyPromise对象
result.then(res => {
resolve(res);
}, err => {
reject(err);
})
} else {
//结果的对象状态为成功
resolve(result);
}
} catch (error) {
reject(e);
}
然后就是优化,基本上每个判断都会有这样一串代码,所以这里把他给分装一下,单独做一块
try {
//执行成功的回调函数
//成功的结果就是当前promise实例对象的PromiseResult
//获取回调函数的执行结果
let result = onResolved(_this.PromiseResult);
//根据返回的结果判断这个promise的返回状态和值
if (result instanceof MyPromise) {
//如果是 MyPromise对象
result.then(res => {
resolve(res);
}, err => {
reject(err);
})
} else {
//结果的对象状态为成功
resolve(result);
}
} catch (e) {
reject(e);
}
then最终的结果:
then(onResolved, onRejected) {
const _this = this;
//调用回调函数 通过当前promise实例对象的PromiseState判断
return new MyPromise((resolve, reject) => {
//封装函数
function callBack(fn) {
try {
//获取回调函数的执行结果
let result = fn(_this.PromiseResult);//注意这里的this指向
if (result instanceof MyPromise) {
//如果是 MyPromise对象
result.then(res => {
resolve(res);
}, err => {
reject(err);
})
} else {
//结果的对象状态为成功
resolve(result);
}
} catch (e) {
reject(e);
}
}
if (this.PromiseState === FULFILLED) {//成功
callBack(onResolved);
} else if (this.PromiseState === REJECTED) {//失败
callBack(onRejected);
} else if (this.PromiseState === PENDING) {//待定
//保存回调函数
this.callBacks.push({
onResolved: function () {
callBack(onResolved);
},
onRejected: function () {
callBack(onRejected);
}
})
}
})
}
2.4、catch方法的异常穿透和值传递
2.4.1、原型上定义一个catch的方法
catch是promise失败的回调,和then的第二个参数函数是一样的
catch的形参也是一个函数
MyPromise.prototype = {
constructor: MyPromise,
/*添加 then() */
then(onResolved, onRejected) {
const _this = this;
//调用回调函数 通过当前promise实例对象的PromiseState判断
return new MyPromise((resolve, reject) => {
//封装函数
function callBack(fn) {
try {
//获取回调函数的执行结果
let result = fn(_this.PromiseResult);
if (result instanceof MyPromise) {
//如果是 MyPromise对象
result.then(res => {
resolve(res);
}, err => {
reject(err);
})
} else {
//结果的对象状态为成功
resolve(result);
}
} catch (e) {
reject(e);
}
}
if (this.PromiseState === FULFILLED) {//成功
callBack(onResolved);
} else if (this.PromiseState === REJECTED) {//失败
callBack(onRejected);
} else if (this.PromiseState === PENDING) {//待定
//保存回调函数
this.callBacks.push({
onResolved: function () {
callBack(onResolved);
},
onRejected: function () {
callBack(onRejected);
}
})
}
})
},
/* 添加catch方法 */
catch(onRejected) {
return this.then(undefined, onRejected);
}
}
2.4.2、异常穿透
所谓异常穿透,如果说promise的状态修改为失败,那即使实例对象调用了多个then都不会执行,直接执行catch,并且在then中返回的promise对象是失败的结果,也会有异常穿透
let p = new Promise((resolve, reject) => {
reject("error");
})
p.then(res => {
console.log(111);
}).then(res => {
console.log(222);
}).then(res => {
console.log(333);
}).catch(err => {
console.warn(err);
})
console.log(p);
先来看看原生的,这里可以看到then方法都没执行
再来看看自己封装的,可以看到已经报错了
其实这个原因很简单,就是如果此时promise的状态为失败,就肯定会去调then的第二个参数,然后再去reject方法中遍历执行,而此时,then中的第二参数没有定义,所以就报了函数未定义的错误。但是原生的promise允许不传第二个回调,所以我们也来实现一下
然后我们又回到这个then方法,在这里加一个判断就可以实现异常穿透了
2.4.3、值传递
值传递就是说,调用then()里面啥都不写,也能实现传递
let p = new Promise((resolve, reject) => {
resolve("error");
})
p.then()
.then(res => {
console.log(222);
}).then(res => {
console.log(333);
}).catch(err => {
console.warn(err);
})
console.log(p);
先看看原生的
再来看看自己的,又开始报错了,和之前一样也是说方法未定义
其实原因也是一样的,之前是第二个回调未定义,现在是两个回调都没定义,那就是两个undefined,显然不符合then内部的一个逻辑,都无法执行了。
又回到这个then方法,在这里加一个判断,和上面一样,反正就是不让函数参数为undefined
2.5、resolve方法封装
这个resolve不是之前的那个resolve,这个resolve是定义在构造函数本身的一个方法,只能通过构造函数本身自己调用的,实例对象不能调用
resolve是构造函数本身的方法
Promise.resolve()等价于new Promise(resolve => resolve())
无参返回的是一个结果为undefined并且状态为成功的promise对象
参数是非promise对象,那then返回结果是结果为这个形参的返回值并且状态为成功的promise对象
参数是promise对象,那这个promise对象的返回结果就是这个then的返回结果
参数抛出异常,那then返回的是一个结果为抛出异常的值并且状态为失败的promise对象
MyPromise.resolve = function (value) {
return new MyPromise((resolve, reject) => {
//老规矩
try {
if (value instanceof MyPromise) {
value.then(res => {
resolve(res);
}, err => {
reject(err);
})
} else {
resolve(value);
}
} catch (error) {
reject(error);
}
})
}
2.6、reject方法封装
reject和resolve一样也构造函数本身的方法,同样也是返回promise,但是不同的是,不管参数是什么,返回的都是失败的promise的对象
MyPromise.reject = function (value) {
return new MyPromise((resolve, reject) => {
reject(value);
})
}
2.7、all方法封装
all()也是构造函数本身的方法,不同的是:
参数是有promise对象为元素组成的元素
只有参数的实例全部都是成功的,返回值也是由成功的promise的对象组成的数组
只要参数中有一个实例对象是失败的,那这个方法返回的就是失败的,失败的结果就是数组中失败的实例的结果
MyPromise.all = function (promises) {
return new MyPromise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(res => {
//不可以这样直接调
resolve();
}, err => {
reject();
})
}
})
}
不可以成功就调成功,如果数组里面的第一个实例成功就是调成功,那如果第二个失败了呢,在原生promise中,就会显示失败的结果,但是如果像上面这样调,那还会显示成功的结果,这显然不行
所以要判断每一个对象的状态都是否为成功,再去调用resolve()
MyPromise.all = function (promises) {
return new MyPromise((resolve, reject) => {
//声明计数变量
let count = 0;
//保存每个实例成功的结果
let resolveBacks = [];
for (let i = 0; i < promises.length; i++) {
promises[i].then(res => {
//成功
count++;
//将当前promise对象成功的结果存入数组中
resolveBacks[i] = res;
//如果count的数量等于promises的数量,就说明全部成功了
if (count === promises.length) {
resolve(resolveBacks);
}
}, err => {
reject(err);
})
}
})
}
resolveBacks[i] = res 这行代码不推荐直接push,all里面的返回的数组是和参数一样的顺序,因为promise每个都是异步的,所以执行的时间不同,而push就是谁先调,谁就先进去数组,显然不符合all的特性
2.8、race方法封装
race()也是构造函数本身的方法,语法和all()类似,不同的是:他参数里的数组里的对象谁先改变状态,那这个方法就返回谁的状态和结果值
MyPromise.race = function (promises) {
return new MyPromise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(res => {
//只要进来,那这个方法的返回状态就是成功的
resolve(res);
}, err => {
//只要进来,那这个方法的返回状态就是失败的
reject(err);
})
}
})
}
2.9、回调函数的异步执行
所谓回调函数的异步执行,话不多说,直接那原生promise看一下就知道了
let p = new Promise((resolve, reject) => {
resolve("成功");
console.log(111);
})
p.then(res => {
console.log(222);
})
console.log(333);
一看就知道then()方法执行的最慢,那肯定就不是同步的,不是同步那就是异步,promise的then()是属于异步中的微任务
再来看一下自己封装的,很有顺序的111,222,333
所以我们来到then方法内部,让他变成异步的就行
在成功和失败这个地方加入queueMicrotask(),这个方法是让他内部的代码块为微任务执行,定时器的话也可以,但是,定时器是属于宏任务的,不符合这个方法的任务队列
还有一个地方要加 在构造函数里面的resolve和reject的方法中成功和失败的回调,也异步调用
3、完结
/* 定义promise的三个状态 */
const PENDING = 'pending'; //待定
const FULFILLED = 'fulfilled'; //成功
const REJECTED = 'rejected'; //失败
function MyPromise(executor) {
//添加属性 起始值
this.PromiseState = PENDING;
this.PromiseResult = undefined;
//保存回调函数
this.callBacks = [];
//保存实例对象的值
const _this = this;
//resolve
const resolve = function (data) {
//判断状态
if (_this.PromiseState !== PENDING) return;
//1.修改对象的成功状态 (PromiseState);
_this.PromiseState = FULFILLED;
//2.设置对象结果值 (PromiseResult);
_this.PromiseResult = data;
//调用成功的回调
queueMicrotask(() => {
_this.callBacks.forEach(item => {
item.onResolved();
})
})
}
//reject
const reject = function (data) {
//判断状态
if (_this.PromiseState !== PENDING) return;
//1.修改对象的失败状态 (PromiseState);
_this.PromiseState = REJECTED;
//2.设置对象结果值 (PromiseResult);
_this.PromiseResult = data;
//调用失败的回调
queueMicrotask(() => {
_this.callBacks.forEach(item => {
item.onRejected();
})
})
}
try {
//同步调用 executor()
executor(resolve, reject);
} catch (e) {
//抛出异常就调用reject修改状态为失败
//抛出的数据 e 就promise失败的结果值
reject(e)
}
}
/* resolve方法 */
MyPromise.resolve = function (value) {
return new MyPromise((resolve, reject) => {
console.log(value);
//老规矩
try {
if (value instanceof MyPromise) {
value.then(res => {
resolve(res);
}, err => {
reject(err);
})
} else {
console.log(1);
resolve(value);
}
} catch (error) {
reject(error);
}
})
}
/* reject */
MyPromise.reject = function (value) {
return new MyPromise((resolve, reject) => {
reject(value);
})
}
/* all */
MyPromise.all = function (promises) {
return new MyPromise((resolve, reject) => {
//声明计数变量
let count = 0;
//保存每个实例成功的结果
let resolveBacks = [];
for (let i = 0; i < promises.length; i++) {
promises[i].then(res => {
//成功
count++;
//将当前promise对象成功的结果存入数组中
resolveBacks[i] = res;
//如果count的数量等于promises的数量,就说明全部成功了
if (count === promises.length) {
resolve(resolveBacks);
}
}, err => {
reject(err);
})
}
})
}
/* race */
MyPromise.race = function (promises) {
return new MyPromise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(res => {
//只要进来,那这个方法的返回状态就是成功的
resolve(res);
}, err => {
//只要进来,那这个方法的返回状态就是失败的
reject(err);
})
}
})
}
/* MyPromise.prototype */
MyPromise.prototype = {
constructor: MyPromise,
/*添加 then() */
then(onResolved, onRejected) {
const _this = this;
//判断回调函数参数
if (typeof onRejected !== "function") {//相当于一个默认值
onRejected = error => {
throw error;
}
}
if (typeof onResolved !== "function") {
onResolved = value => value;
//相当于
// onResolved = function (value) {
// return value;
// }
}
//调用回调函数 通过当前promise实例对象的PromiseState判断
return new MyPromise((resolve, reject) => {
//封装函数
function callBack(fn) {
try {
//获取回调函数的执行结果
let result = fn(_this.PromiseResult);
if (result instanceof MyPromise) {
//如果是 MyPromise对象
result.then(res => {
resolve(res);
}, err => {
reject(err);
})
} else {
//结果的对象状态为成功
resolve(result);
}
} catch (e) {
reject(e);
}
}
if (this.PromiseState === FULFILLED) {//成功
queueMicrotask(() => {
callBack(onResolved);
})
} else if (this.PromiseState === REJECTED) {//失败
queueMicrotask(() => {
callBack(onRejected);
})
} else if (this.PromiseState === PENDING) {//待定
//保存回调函数
this.callBacks.push({
onResolved: function () {
callBack(onResolved);
},
onRejected: function () {
callBack(onRejected);
}
})
}
})
},
/* 添加catch方法 */
catch(onRejected) {
return this.then(undefined, onRejected);
}
}
这个promise的封装终于结束了,其实大家回过头去看,也不会觉得会很难,主要就是then()这个地方有点绕,但是理清楚了也还是没问题的。