一、promise基础用法
Promise
对象是一个构造函数,用来生成Promise
实例,promise对象特点:
- 有三种状态:
pending
(进行中)、fulfilled
(已成功)和rejected
(已失败) Promise
构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
。- Promise 的原型上定义着 then 方法。
Promise
原生基础实例
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
resolve
函数的作用是,将Promise
对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject
函数的作用是,将Promise
对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise
实例生成以后,可以用then
方法分别指定resolved
状态和rejected
状态的回调函数。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
实现基础promise
基础框架
class Promise {
_pending = 'pending'; // 进行在
_fulfilled = 'fulfilled'; // 完成
_rejected = 'rejected'; // 失败
// 状态描述 pending fulfilled rejected
_state = this._pending; // 状态
value = null; // 保存数据
reason = null;
constructor(executor) {
try {
// bind避免因为函数外执行,方法内部this指向改变无法
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}
resolve(value) {
}
reject(reason) {
}
}
Promise.prototype.then = function(onFulfilled, onRejected) {
}
Promise
构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
。实现resolve和rejected
class Promise {
_pending = 'pending'; // 进行在
_fulfilled = 'fulfilled'; // 完成
_rejected = 'rejected'; // 失败
// 状态描述 pending fulfilled rejected
_state = this._pending; // 状态
value = null; // 保存数据
reason = null;
constructor(executor) {
try {
// bind避免因为函数外执行,方法内部this指向改变无法
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}
// `resolve`函数的作用是,将`Promise`对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
resolve(value) {
if(this._state === this._pending) {
this.value = value;
this._state = this._fulfilled;
}
}
// reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
reject(reason) {
if(this._state === this._pending) {
this.reason = reason;
this._state = this._rejected;
}
}
}
实现then
then方法原生实例
promise.then(function(value) {
// success
}, function(error) {
// failure
});
then
方法可以接受两个回调函数作为参数。第一个回调函数是Promise
对象的状态变为resolved
时调用,第二个回调函数是Promise
对象的状态变为rejected
时调用。这两个函数都是可选的,不一定要提供。
代码实现
Promise.prototype.then = function(onFulfilled, onRejected) {
// 当传入的then回调函数为空的时候。。创建对应的空函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {};
onRejected = typeof onRejected === 'function' ? onRejected : () => {};
if(this._state === this._fulfilled) {
onFulfilled(this.value);
} else if(this._state === this._rejected) {
onRejected(this.reason);
}
}
二、实现异步
同步的
let myPromise = new Promise((resolve, reject) => { resolve(1) });
p.then((data) => console.log(data)) // 1
试下异步
let myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolve1');
})
});
p.then((data) => console.log(data)) // 无输出
因为setTimeout是异步的,在调用then方法时候_state还是pending,不会执行onFulfilled和onRejected。
开始改造: 在执行then
方法时如果还在等待态 pending
,就把回调函数临时寄存到队列(就是一个数组)里,当状态发生改变时依次从数组中取出执行。
1、新增两个数组,onResolvedCallbacks,onRejectedCallbacks,用于存放成功和失败的回调函数
class Promise {
_pending = 'pending';
_fulfilled = 'fulfilled';
_rejected = 'rejected';
_state = this._pending; // 状态
value = null; // 保存数据
reason = null;
// 保存尚未fulfilled的then中的回调函数(异步)
onResolvedCallbacks = [];
// 保存尚未rejected的then中的回调函数(异步)
onRejectedCallbacks = [];
// ...
}
2、then
方法执行时如果状态是等待态,就将其回调函数存入对应数组
Promise.prototype.then = function(onFulfilled, onRejected) {
// 当传入的then回调函数为空的时候。。创建对应的空函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {};
onRejected = typeof onRejected === 'function' ? onRejected : () => {};
if(this._state === this._fulfilled) {
onFulfilled(this.value);
} else if(this._state === this._rejected) {
onRejected(this.reason);
} else if(this._state === this._pending) { // 新增等待态判断,此时异步代码还未走完,回调入数组队列
this.onResolvedCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}
}
3、resolve
和 reject
方法中调用onResolvedCallbacks,onRejectedCallbacks
resolve(value) {
if(this._state === this._pending) {
this.value = value;
this._state = this._fulfilled;
// 判断成功回调是否存在,如果存在就调用
// 循环回调数组. 把数组前面的方法弹出来并且直接调用
// shift方法是在数组中删除值,每执行一个就删除一个,最终变为0
while(this.onResolvedCallbacks.length) {
this.onResolvedCallbacks.shift()(value);
}
}
}
reject(reason) {
if(this._state === this._pending) {
this.reason = reason;
this._state = this._rejected;
// 判断失败回调是否存在,如果存在就调用
// 循环回调数组. 把数组前面的方法弹出来并且直接调用
while(this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(reason);
}
}
}
这样已经可以输出了异步的数据了。
但是加上执行顺序,就出问题了
console.log(1)
function action() {
console.log(2)
new Promise((resolve, reject) => {
console.log(3)
resolve('resolve1');
}).then((data) => {
console.log('then:' + data)
})
console.log('jieshu')
}
action();
实际输出: 1->2->3->then:resolve1->jieshu;
本来应该输出: 1->2->3->jieshu->jieshu;
4、实现resolve
和rejected
异步,使用queueMicrotask
resolve(value) {
if(this._state !== this._pending) return;
// resolve实现异步(微任务)
queueMicrotask(() => {
this.value = value;
this._state = this._fulfilled;
// 判断成功回调是否存在,如果存在就调用
// 循环回调数组. 把数组前面的方法弹出来并且直接调用
// shift方法是在数组中删除值,每执行一个就删除一个,最终变为0
while(this.onResolvedCallbacks.length) {
this.onResolvedCallbacks.shift()(value);
}
// 遍历执行成功回调
// this.onResolvedCallbacks.forEach(callBack => callBack(value))
// })
}
// reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
reject(reason) {
if(this._state !== this._pending) return;
// 实现异步(微任务)
queueMicrotask(() => {
this.reason = reason;
this._state = this._rejected;
// 判断失败回调是否存在,如果存在就调用
// 循环回调数组. 把数组前面的方法弹出来并且直接调用
while(this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(reason);
}
// 遍历执行失败回调
// this.onRejectedCallbacks.forEach(callBack => callBack(reason));
})
}
三、实现链式调用then
then
方法返回的是一个新的Promise
实例(注意,不是原来那个Promise
实例)。因此可以采用链式写法,即then
方法后面再调用另一个then
方法。
then
的需求是什么,这需要仔细看 Promises/A+ 规范中对then
方法的返回值定义及 Promise 解决过程,如下:
- 首先
then
方法必须返回一个promise
对象(划重点) - 如果
then
方法中返回的是一个普通值(如 Number、String 等)就使用此值包装成一个新的 Promise 对象返回 - 如果
then
方法中没有return
语句,就返回一个用 Undefined 包装的 Promise 对象 - 如果
then
方法中出现异常,则调用失败态方法(reject)跳转到下一个then
的 onRejected - 如果
then
方法没有传入任何回调,则继续向下传递(值穿透) - 如果
then
方法中返回了一个 Promise 对象,那就以这个对象为准,返回它的结果
1、then返回一个新的promise
Promise.prototype.then = function(onFulfilled, onRejected) {
// 当传入的then回调函数为空的时候。。创建对应的空函数,当then方法中没有回调时,我们需要把接收到的值继续向下传递
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
onRejected = typeof onRejected === 'function' ? onRejected : (err) => { throw err};
// then方法会返回一个新的promise对象,以便使用then方法接收结果
return new Promise((resolve, reject) => {
// 封装一个成功时执行的函数
let fulfilled = value => {
try {
// 拿到返回值resolve出去
let result = onFulfilled(value);
resolve(result);
} catch (error) {
reject(error)
}
};
// 封装一个失败时执行的函数
let rejected = err => {
try {
// 拿到返回值reject出去
let result = onRejected(value);
reject(result);
} catch (error) {
reject(error)
}
};
if(this._state === this._fulfilled) {
fulfilled(this.value);
} else if(this._state === this._rejected) {
rejected(this.reason);
} else if(this._state === this._pending) { // 新增等待态判断,此时异步代码还未走完,回调入数组队列
this.onResolvedCallbacks.push(fulfilled);
this.onRejectedCallbacks.push(rejected);
}
})
2、then方法如果返回一个promise,要等返回的promise状态变化后才执行下面的then
// 判断是否是Promise
isPromise(promiseNew, callValue, resolve, reject) {
// 如果相等了,说明return的是自己,抛出类型错误并返回
if(promiseNew === callValue) {
return reject('请避免Promise循环引用')
}
if(callValue instanceof Promise) {
// 如果当前回调函数返回Promise对象,必须等待其状态改变后在执行下一个回调
// 调用上一次then中成功回调返回的型promise的then方法
callValue.then(resolve, reject);
} else {
// 如果是一个非Promise数值,那么就直接调用返回 ---- promise特性
resolve(callValue);
}
}
tthen方法调用isPromise
Promise.prototype.then = function(onFulfilled, onRejected) {
// 当传入的then回调函数为空的时候。。创建对应的空函数,当then方法中没有回调时,我们需要把接收到的值继续向下传递
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
onRejected = typeof onRejected === 'function' ? onRejected : (err) => { throw err};
// then方法会返回一个新的promise对象,以便使用then方法接收结果
let promiseNew = new Promise((resolve, reject) => {
// 封装一个成功时执行的函数
let fulfilled = value => {
try {
// 拿到返回值resolve出去
let result = onFulfilled(value);
// 判断是否是Promise
/*
判断返回的是否是promise,这里的resolve,reject是
返回的新promise的成功/失败的接收函数
*/
this.isPromise(promiseNew, result, resolve, reject);
} catch (error) {
reject(error)
}
};
// 封装一个失败时执行的函数
let rejected = err => {
try {
// 拿到返回值reject出去
let result = onRejected(err);
this.isPromise(promiseNew,result, resolve, reject);
} catch (error) {
reject(error)
}
};
if(this._state === this._fulfilled) {
fulfilled(this.value);
} else if(this._state === this._rejected) {
rejected(this.reason);
} else if(this._state === this._pending) { // 新增等待态判断,此时异步代码还未走完,回调入数组队列
this.onResolvedCallbacks.push(fulfilled);
this.onRejectedCallbacks.push(rejected);
}
})
return promiseNew;
}
四、catch实现
Promise.prototype.catch()
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。
Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected)
}
五、Promise.resove(),reject()
- 如果参数就是一个promise对象,直接返回,如果是一个值,那么需要生成一个promise对象,把值进行返回
- 是Promise类的一个静态方法
class Promise {
...//
static resolve(callBack) {
let P = this.constructor; // Promise
if(callBack instanceof P) {
// 如果是promise对象,就直接返回
return callBack
} else {
// 如果是值就返回一个promise对象,并返回值
return new Promise(resolve => resolve(callBack));
}
}
...//
}
Promise.reject()的实现
static reject(callBack) {
let P = this.constructor; // Promise
if(callBack instanceof P) {
return callBack
} else {
return new Promise((resolve,reject) => reject(callBack));
}
}
六、finally实现
不管promise
最后的状态,在执行完then
或catch
指定的回调函数以后,都会执行finally
方法指定的回调函数。
- 无论当前最终状态是成功还是失败,finally都会执行
- 我们可以在finally方法之后调用then方法拿到结果
- 这个函数是在原型对象上用的
Promise.prototype.finally = function(callback) {
// 如何拿到当前的promise的状态,使用then方法,而且不管怎样都返回callback
// 而且then方法就是返回一个promise对象,那么我们直接返回then方法调用之后的结果即可
// 我们需要在回调之后拿到成功的回调,所以需要把value也return
// 失败的回调也抛出原因
// 如果callback是一个异步的promise对象,我们还需要等待其执行完毕,所以需要用到静态方法resolve
let P = this.constructor; // Promise
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
}
七、all方法
Promise.all()
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
Promise.all()
方法接受一个数组作为参数,数组元素都是 Promise 实例,如果不是,就会先调用Promise.resolve
方法,将参数转为 Promise 实例。
// Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例
static all(list) {
// 返回新的Promise
return new Promise((resolve, reject) => {
let result = []; //存储结果
for(let [i, elem] of list.entries()) {
// Promise.all()方法接受一个数组作为参数,参数都是promise实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例
let p = elem instanceof Promise ? elem : Promise.resolve(elem);
p.then(res => {
result.push(res);
// 全部resolve时返回的Promise状态就变成resolve
if(result.length === list.length) {
resolve(result);
}
}, err => {
// 有一个被rejected时返回的Promise状态就变成rejected
reject(err)
})
}
})
}
八、race
Promise.race()
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例
// Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例
static race(list) {
// 返回新的Promise
return new Promise((resolve, reject) => {
for(let [i, elem] of list.entries()) {
// Promise.race()方法接受一个数组作为参数,参数都是promise实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例
let p = elem instanceof Promise ? elem : Promise.resolve(elem);
p.then(res => {
// 有一个实例resolve时返回的Promise状态就变成resolve
return resolve(res);
}, err => {
// 有一个被rejected时返回的Promise状态就变成rejected
reject(err)
})
}
})
}
九、全部代码
class Promise {
_pending = 'pending';
_fulfilled = 'fulfilled';
_rejected = 'rejected';
_state = this._pending; // 状态
value = null; // 保存数据
reason = null;
// 保存尚未fulfilled的then中的回调函数(异步)
onResolvedCallbacks = [];
// 保存尚未rejected的then中的回调函数(异步)
onRejectedCallbacks = [];
constructor(executor) {
try {
// bind避免因为函数外执行,方法内部this指向改变无法
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}
// resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
resolve(value) {
if(this._state !== this._pending) return;
// resolve实现异步(微任务)
queueMicrotask(() => {
this.value = value;
this._state = this._fulfilled;
// 判断成功回调是否存在,如果存在就调用
// 循环回调数组. 把数组前面的方法弹出来并且直接调用
// shift方法是在数组中删除值,每执行一个就删除一个,最终变为0
while(this.onResolvedCallbacks.length) {
this.onResolvedCallbacks.shift()(value);
}
})
}
// reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
reject(reason) {
if(this._state !== this._pending) return;
// 实现异步(微任务)
queueMicrotask(() => {
this.reason = reason;
this._state = this._rejected;
// 判断失败回调是否存在,如果存在就调用
// 循环回调数组. 把数组前面的方法弹出来并且直接调用
while(this.onRejectedCallbacks.length) {
this.onRejectedCallbacks.shift()(reason);
}
})
}
// 判断是否是Promise
isPromise(promiseNew, callValue, resolve, reject) {
// 如果相等了,说明return的是自己,抛出类型错误并返回
if(promiseNew === callValue) {
return reject('请避免Promise循环引用')
}
if(callValue instanceof Promise) {
// 如果当前回调函数返回Promise对象,必须等待其状态改变后在执行下一个回调
// 调用上一次then中成功回调返回的型promise的then方法
callValue.then(resolve, reject);
} else {
// 如果是一个非Promise数值,那么就直接调用返回 ---- promise特性
resolve(callValue);
}
}
static resolve(callBack) {
let P = this.constructor; // Promise
if(callBack instanceof P) {
// 如果是promise对象,就直接返回
return callBack
} else {
// 如果是值就返回一个promise对象,并返回值
return new Promise(resolve => resolve(callBack));
}
}
static reject(callBack) {
let P = this.constructor; // Promise
if(callBack instanceof P) {
return callBack
} else {
return new Promise((resolve,reject) => reject(callBack));
}
}
// Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例
static all(list) {
// 返回新的Promise
return new Promise((resolve, reject) => {
let result = []; //存储结果
for(let [i, elem] of list.entries()) {
// Promise.all()方法接受一个数组作为参数,参数都是promise实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例
let p = elem instanceof Promise ? elem : Promise.resolve(elem);
p.then(res => {
result.push(res);
// 全部resolve时返回的Promise状态就变成resolve
if(result.length === list.length) {
resolve(result);
}
}, err => {
// 有一个被rejected时返回的Promise状态就变成rejected
reject(err)
})
}
})
}
// Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例
static race(list) {
// 返回新的Promise
return new Promise((resolve, reject) => {
for(let [i, elem] of list.entries()) {
// Promise.race()方法接受一个数组作为参数,参数都是promise实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例
let p = elem instanceof Promise ? elem : Promise.resolve(elem);
p.then(res => {
// 有一个实例resolve时返回的Promise状态就变成resolve
return resolve(res);
}, err => {
// 有一个被rejected时返回的Promise状态就变成rejected
reject(err)
})
}
})
}
}
Promise.prototype.then = function(onFulfilled, onRejected) {
// 当传入的then回调函数为空的时候。。创建对应的空函数,当then方法中没有回调时,我们需要把接收到的值继续向下传递
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
onRejected = typeof onRejected === 'function' ? onRejected : (err) => { throw err};
// then方法会返回一个新的promise对象,以便使用then方法接收结果
let promiseNew = new Promise((resolve, reject) => {
// 封装一个成功时执行的函数
let fulfilled = value => {
try {
// 拿到返回值resolve出去
let result = onFulfilled(value);
// 判断是否是Promise
/*
判断返回的是否是promise,这里的resolve,reject是
返回的新promise的成功/失败的接收函数
*/
this.isPromise(promiseNew, result, resolve, reject);
} catch (error) {
reject(error)
}
};
// 封装一个失败时执行的函数
let rejected = err => {
try {
// 拿到返回值reject出去
let result = onRejected(err);
this.isPromise(promiseNew,result, resolve, reject);
} catch (error) {
reject(error)
}
};
if(this._state === this._fulfilled) {
fulfilled(this.value);
} else if(this._state === this._rejected) {
rejected(this.reason);
} else if(this._state === this._pending) { // 新增等待态判断,此时异步代码还未走完,回调入数组队列
this.onResolvedCallbacks.push(fulfilled);
this.onRejectedCallbacks.push(rejected);
}
})
return promiseNew;
}
Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected)
}
Promise.prototype.finally = function(callback) {
// 如何拿到当前的promise的状态,使用then方法,而且不管怎样都返回callback
// 而且then方法就是返回一个promise对象,那么我们直接返回then方法调用之后的结果即可
// 我们需要在回调之后拿到成功的回调,所以需要把value也return
// 失败的回调也抛出原因
// 如果callback是一个异步的promise对象,我们还需要等待其执行完毕,所以需要用到静态方法resolve
let P = this.constructor; // Promise
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
}
参考: