JavaScript系列 - Promise
不止一次的学习Promise, 真正学懂Promise是每次遇到问题之后,反反复复的查阅资料,学习就是如此,重复之
是什么?
- 不多解释,是什么的问题,随处可查,解决什么问题,想必前端人员再了解不过了。
理念
- 观察者模式
const P = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('result');
}, 1000);
})
P.then(res => console.log, err => console.log(err));
我们来分析Promise的调用流程:
- Promise的构造方法接受一个executor() , 在new Promise() 的时候立刻执行这个回调;
- executor() 内部的异步任务被放入宏/微任务队列,等待执行;
- then() 被执行,收集成功/失败回调,放入成功/失败队列;
- executor() 的异步任务被执行,触发resolve/reject,从成功/失败队列中取出回调依次执行; 这就是一个典型的
观察者模式,这种收集依赖 -> 触发通知 -> 取出执行依赖的方式,就是观察者模式的实现,在Promise中,执行顺序是:then收集依赖 -> 异步触发resolve -> resolve执行依赖。 我们知道了这一点之后,就能在脑海中勾勒出平时工作中写的业务代码,假如我们封装了一个用于公共请求的fetch方法,如下:
import fetch from './utils';
fetch('/abc/def', get).then(res => {
console.log(res);
setState({ // react中的setState
data: res;
})
})
上面的代码在业务中及其常见,我们来解读一下:
- fetch方法的参数为请求数据的url,以及请求方式,这个方法内部使用axios实现,也就是利用Promise来实现异步任务
- 上面我们说到,then方法收集依赖,我们可以想到,这个方法是在fetch请求完毕才会调用的,这样,Promise就完成了它的任务,异步任务就这样很优雅的被实现。
实现
其实,如果不知道Promise中是如何实现这种异步回调的话,理解起来还是蛮费劲,只是知道了字面意思,那么我们先实现一个简单的Promise来大概了解一下
class MyPromise {
// 构造函数接收一个方法
constructor(executor) {
this._resolveQueue = []; // then方法收集成功回调队列
this._rejectQueue = []; // then方法收集的失败回调队列
// 箭头函数可以避免this指向为undefined的问题
let _resolve = (val) => {
while (this._resolveQueue.length) {
// 从成功的队列中取出回调依次执行
const callback = this._resolveQueue.shift();
callback(val);
}
};
// 同上
let _reject = (val) => {
while (this._rejectQueue.length) {
const callback = this._rejectQueue.shift();
callback(val);
}
};
// executor函数立即执行,并传入_resolve, _reject方法
executor(_resolve, _reject);
}
// then方法收集成功或者失败方法 push进队列;
then(resolveFn, rejectFn) {
this._resolveQueue.push(resolveFn);
this._rejectQueue.push(rejectFn);
}
}
我们使用观察者模式简单的实现了一个Promise,使我们能够在then方法的回调里取得异步操作的返回值
Promise A+ 规范
- 两条核心规则
- Promise本质是一个状态机,且状态只能为以下三种:
Pending(等待态)、Fulfilled(执行态)、Rejected(拒绝态),状态的变更是单向的,只能从 Pending -> Fulfilled 或者 Pending -> Rejected- then方法接收两个可选参数,分别对应状态改变时触发的回调。then方法返回一个Promise, then方法可以被同一个promise调用多次 我们补充Promise规范如下:
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
// 构造函数接收一个方法
constructor(executor) {
this.state = PENDING; // Promise初始状态
this._resolveQueue = []; // then方法收集成功回调队列
this._rejectQueue = []; // then方法收集的失败回调队列
// 箭头函数可以避免this指向为undefined的问题
let _resolve = (val) => {
if (this.state !== PENDING) return; // 如果状态不是pending, 那么return;
this.state = FULFILLED; // 变更状态
while (this._resolveQueue.length) {
// 从成功的队列中取出回调依次执行
const callback = this._resolveQueue.shift();
callback(val);
}
};
// 同上
let _reject = (val) => {
if (this.state !== PENDING) return; // 如果状态不是pending, 那么return;
this.state = REJECTED; // 变更状态
while (this._rejectQueue.length) {
const callback = this._rejectQueue.shift();
callback(val);
}
};
// executor函数立即执行,并传入_resolve, _reject方法
executor(_resolve, _reject);
}
// then方法收集成功或者失败方法 push进队列;
then(resolveFn, rejectFn) {
this._resolveQueue.push(resolveFn);
this._rejectQueue.push(rejectFn);
}
}
then的链式调用
如何实现链式调用:
then方法需要返回一个Promise,这样才能找到then方法,所以我们会把then方法的返回值包装成Promise;then的回调需要拿到上一个then的返回值;then的回调需要顺序执行,我们要等待当前Promise状态变更后,再执行下一个then收集的回调,这就要求我们对then的返回值进行分类处理
then(resolveFn, rejectFn) {
// 为了实现then方法的链式调用返回一个Promise
return new MyPromise((resolve, reject) => {
const fulfilledFn = (value) => {
try {
// 执行当前的Promise成功回调,并获取返回值, // 等于外部的resolve值, 或者then方法的return 值
// 这个方法以前是在_resolveQueue队列中执行,现在提前执行,就是为了知道其返回值;
let x = resolveFn(value);
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x);
} catch (error) {
reject(error);
}
};
this._resolveQueue.push(fulfilledFn);
const rejectedFn = (error) => {
try {
let x = rejectFn(error);
x instanceof MyPromise ? x.then(resolve, reject) : reject(x);
} catch (error) {
reject(error);
}
};
this._rejectQueue.push(rejectedFn);
});
}
Promise.prototype.catch()
catch()方法返回一个Promise, 并且处理拒绝的情况,它的行为与调用Promise.prototype.then(undefined, onRejected相同). // catch方法其实就是执行一下then的第二个回调
catch(rejectFn) { return this.then(undefined, rejectFn); }
Promise.prototype.finally()
finally() 方法返回一个Promise, 在promise结束时,无论结果是成功或者失败,都会执行指定的回调函数。在finally之后,还可以继续then. 并且会将值原封不动的传递给后面的then
finally(callback) { return this.then( value => MyPromise.resolve(callback()).then(() => value), reason => MyPromise.resolve(callbacl()).then(() => { throw reason }) ) }
Promise.resolve()
Promise.resolve(value)方法返回一个以给定值解析后的Promise对象。如果该值为promise, 返回这个promise; 如果这个值是thenable(即带有then方法),返回的promise会跟随这个thenable的对象,采用它的最终状态,否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。
static resolve(value) { if (value instanceof MyPromise) return value; return new Promise(resolve => resolve(value)); }
Promise.reject()
Promise.reject() 方法返回一个带有拒绝原因的Promise对象
static reject(reason) { return new MyPromise((resolve, reject) => reject(reason)) }
Promise.all()
Promise.add(iterable) 方法返回一个Promise实例,此实例在iterable参数内所有的promise都完成或参数中不包含promise时回调完成,如果参数中promise有一个失败,此实例回调失败,失败原因是第一个失败promise的结果
static all(promiseArr) { let index = 0; let result = []; return new MyPromise((resolve,reject) => { promiseArr.forEach((p,i) => { MyPromise.resolve(p).then( val => { index++; result[i] = val; if (index === promiseArr.length) { resolve(result) } }, err => { reject(err); } ) }) }) }
Promise.race()
Promise.race(iterable) 方法返回一个promise, 一旦迭代器中的某个promise解决或者拒绝,返回的promise就会解决或者拒绝
static race(promiseArr) { return new MyPromise((resolve,reject) => { // 同时执行Promise, 如果有一个Promise的状态发生改变,就更新Promise的状态 for (let p of promiseArr) { MyPromise.resolve(p).then( value => { resolve(value); }, err => { reject(err); } ) } }) }
\