一、Promise的使用
1. 什么是Promise
Promise是ES6提供的构造函数,Promise对象用来表示一个异步操作的最终状态(成功或失败),以及该异步操作的结果值。
Promise对象是一个代理对象(代理一个值),被代理的值在Promise对象创建时是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理回调函数,这使得异步方法可以像同步方法那样返回,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的Promise对象。
Promise对象可以解决异步嵌套问题以及多个异步并发问题,但Promise依旧是基于回调的写法,在复杂业务逻辑下可能还会存在嵌套问题,而且无法终止异步。
通过
genertor函数+co库处理Promise异步,可以解决回调嵌套问题,简化then的使用。终极解决方案是ES7中提供的genertor函数+co库的语法糖async-await。
2. Promise对象的内部属性
[[PromiseStatus]]: 内部变量, 存储Promise对象当前的状态值。[[PromiseResult]]: 内部变量,存储失败或成功的结果。
[[PromiseStatus]]状态值 | 含义 |
|---|---|
pending | 初始状态,既不是成功,也不是失败状态。 |
fulfilled | 意味着操作成功完成; 调用resolve函数可以将状态扭转至fulfilled。 |
rejected | 意味着操作失败;调用reject函数或者抛出异常可以将状态扭转至rejected。 |
[[PromiseStatus]]的状态改变后,不能再次被修改。
3. 创建Promise对象
通过new关键字调用Promise构造函数创建一个实例时,需要为构造函数传入一个executor执行器函数,该函数是同步执行的,并且执行时会接收两个参数分别是resolve函数及reject函数。
执行resolve函数可以将Primise对象的状态改为fulfilled成功态,调用resolve函数时传入的参数是Promise对象的成功结果。
执行reject函数可以将Primise对象的状态改为rejected失败态,reject函数的入参是Promise对象的失败结果。
function exector (resolve, reject) {
}
console.log(new Promise(exector));
// Promise {<pending>}
// [[prototype]]: Promise
// [[PromiseStatus]]: pending
// [[PromiseResult]]: undefined
4. executor执行器函数
① executor函数
executor函数是创建Promise对象时的入参,executor函数由程序员定义,在new Promise(executor)时是会立即同步调用传入的executor函数,并且调用时会传递两个参数分别是resolve函数及reject函数。
② resolve
由Promise内部定义, 但由程序员手动调用的函数。
当异步任务成功时,我们应该调用resolve函数,resolve函数被调用,内部会同步修改Promise对象的状态,将其置为fulfilled以及将传入的参数作为成功结果保存,然后异步执行所有onResolved回调函数。
③ reject
由Promise内部定义,当异步任务失败或抛出error时,我们应该调用reject函数, reject函数被调用,内部会同步修改Promise对象的状态,将其置为rejected并将传入的参数作为失败的原因保存,然后异步调用所有onRejected回调函数。
注意:一个失败的
Promise必须要进行捕获即必须要由onRejected回调函数处理错误,否则会报错
5. Promise常用API
① Promise.prototype.then([onResolved] [, onRejected])
then()方法是Promise原型中的方法,该方法可以接受两个可选参数,分别是Promise的状态为处理异步成功或异步失败的回调函数,当Promise对象的状态改变时会异步执行对应状态的回调函数。
then()方法的返回值是一个新的Promise对象,也正是因为返回值是一个Promise对象,因此可以实现链式调用。其返回的Promise对象的状态由回调函数如何执行决定:
- 若
onResolved或onRejected回调函数正常执行,且回调函数的返回值是一个Promise对象,则会立即执行该Promise对象,并且将该Promise对象的状态及结果值作为then()方法返回的Promise对象状态及结果; - 若
onResolved或onRejected回调函数正常执行,则返回的Promise对象为成功态,且回调函数的返回值为Promise对象的成功结果; - 若
onResolved或onRejected回调函数抛出异常,则返回的Promise对象为失败态,且抛出的错误为Promise对象的失败原因。
// 当回调函数中的返回值为Promise对象时,then方法的返回的Promise对象的状态及值,取决于回调中返回的Promise的状态及值
const p = new Promise((res, rej) => {
setTimeout(() => {res(1)}, 1000)
})
p.then(() => {
return Promise.reject('e');;
}).then((v) => {
console.log('fulfilled:' , v);
}, (e) => {
console.log('rejected:' , e);
})
扩展:
基于
then()的返回值取决于其回调的返回值的特点,可以实现通过在回调中返回一个始终为pending状态的Promise对象终止Promise.prototype.then方法的链式调用。Promise.resolve().then(() => { return new Promise(() => {}); // 终止promise })
② Promise.prototype.catch(onRejected])
catch()方法时then()方法的语法糖,相当于then(null, onRejected)。
③ Promise.prototype.finally(onFinally) (ES9提供)
当一个Promise的状态确定后,无论是fulfilled还是rejected,onFinally回调都会执行。
finally()方法返回一个新的Promise对象,若onFinally回调中抛出错误或返回一个失败的Promise,则finally()方法返回的Promise对象的状态为rejected并取其失败原因,否则新的Promise对象与当前执行finally()方法的Promise对象状态保持一致。
new Promise((resolve, reject) => {
reject(1)
}).finally(() => {
console.log('onFinally执行');
}).catch((e) => {
console.log('onRjected执行:', e);
})
// 输出结果:
// onFinally执行
// onRjected执行:1
④ Promise.resolve(value)
Promise.resolve()是Promise构造函数的静态方法,返回一个确定状态Promise对象。
Promise.resolve()用于包装普通值时,是手动创建一个fulfilled状态的Promise对象的快捷方法;Promise.resolve()接收Promise对象时,无论是否是嵌套的Promise都将其展开执行,最终返回一个有状态的Promise对象。
但返回的Promise对象最终状态及值取决于传入的参数:
- 若参数时
Promise,则将传入的Promise的执行,并将最终状态及结果值作为返回的Promise对象的状态及结果; - 若为其它值,则为返回的
Promise对象的成功结果。
⑤ Promise.reject(reason)
Promise.reject()是Promise构造函数的静态方法,始终返回一个失败的Promise对象,是手动创建一个rejected状态的Promise对象的快捷方法。
无论参数reason是不是Promise对象,都不会等待执行该Promise对象。直接将接收的Promise对象作为失败原因。
⑥ Promise.all(iterable)
Promise.all()是Promise提供的静态方法,接收的参数iterable是一个可迭代对象,常为数组类型,返回值是一个新的Promise对象。
Promise.all()用于处理多个异步并发请求,只有当所有异步都成功,其返回的Promise对象才是成功态,成功的结果组成的数组与iterable中的顺序一致;任何一个异步失败则立即返回失败态的Promise对象。
const p1 = new Promise((res) => {
setTimeout(() => res(1), 1000);
})
const p2 = Promise.resolve(2);
Promise.all([p1, 3, p2]).then (d => {
console.log(d);
})
Promise.all('aoi').then((d) => {
console.log(d);
})
// 输出结果:
// [ 'a', 'o', 'i' ]
// [ 1, 3, 2 ]
⑦ Promise.rate(iterable)
Promise.rate()方法是Promise提供的静态方法,其接收的参数和返回值同Promise.all()方法一致。
Promise.rate()方法将参数iterable中第一个得到结果的状态及结果值作为返回的Promise对象的状态和结果。
⑧ Promise.allSettled(iterable)
Promise.allSettled()方法是Promise提供的静态方法,其接收的参数和同Promise.all()方法一致,接受一个可迭代对象,返回一个成功状态的Promise对象。
无论iterable中是否存在失败的Promise,Promise.allSettled()方法始终后返回一个成功的Promise对象。其成功的结果为一个数组,数组中的元素是一个对象,每个对象都包含以下属性:
status:表示该下标的Promise对象的最终状态,值为fulfilled或rejected。value:当状态为fulfilled时,才存在该属性,其值为成功的结果。reason:当状态为rejected时,才存在该属性,其值为失败的原因。
const p1 = new Promise((res) => {
setTimeout(res(1), 1000)
})
const p2 = Promise.reject('a');
const p3 = new Promise((res, rej) => {
setTimeout(() => {
if (Math.random() > 0.5) res(100);
else rej(1000)
}, 1000)
})
Promise.allSettled([p1, p2, p3]).then((data) => {
console.log('data:', data)
}, err => console.log('err: ', err))
// 输出结果
// data: [
// { status: 'fulfilled', value: 1 },
// { status: 'rejected', reason: 'a' },
// { status: 'fulfilled', value: 100 }
// ]
二、Promise源码实现
源码规定详见 Promise A+规范 可以通过
promises-aplus-tests库来校验自己实现的Promise是否符合规范。
1. 简易同步版Promise实现
Promise实例的内部属性PromiseStatus存放状态,初始状态为pending,状态一经改变后不能再次改变;
Promise实例的内部属性PromiseResult存放成功的结果或失败的原因;
执行new Promise(executor)时会立即同步调用传入的executor函数,并且调用时会传递两个参数分别是resolve函数及reject函数;
resolve执行将状态改为成功态,并将参数作为成功结果;reject执行或者抛出异常则将状态改为失败态,并将参数/异常作为失败原因;
then()方法中的回调函数会在状态为fulfilled或rejected时执行对应的回调函数。
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class Promise {
constructor (executor) {
this.PromiseStatus = PENDING;
this.PromiseResult = undefined;
let resolve = (value) => { // 为了拿到this,应使用箭头函数
if (this.PromiseStatus === PENDING) {
this.PromiseStatus = FULFILLED;
this.PromiseResult = value;
}
}
let reject = (reason) => {
if (this.PromiseStatus === PENDING) {
this.PromiseStatus = REJECTED;
this.PromiseResult = reason;
}
}
// 抛出异常则将状态改为失败态,并将异常结果作为失败原因
try {
executor (resolve, reject);
} catch (e) {
reject(e);
}
}
then (onFulfilled, onRejected) {
if (this.PromiseStatus === FULFILLED) {
onFulfilled(this.PromiseResult)
}
if (this.PromiseStatus === REJECTED) {
onRejected(this.PromiseResult)
}
}
}
module.exports = Promise;
2. 基于发布订阅模式处理异步情况
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class Promise {
constructor (executor) {
this.PromiseStatus = PENDING;
this.PromiseResult = undefined;
// 存放异步回调
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if (this.PromiseStatus === PENDING) {
this.PromiseStatus = FULFILLED;
this.PromiseResult = value;
// 发布
this.onFulfilledCallbacks.forEach(fn => fn())
}
}
let reject = (reason) => {
if (this.PromiseStatus === PENDING) {
this.PromiseStatus = REJECTED;
this.PromiseResult = reason;
// 发布
this.onRejectedCallbacks.forEach(fn => fn())
}
}
// 抛出异常则将状态改为失败态,并将异常结果作为失败原因
try {
executor (resolve, reject);
} catch (e) {
reject(e);
}
}
then (onFulfilled, onRejected) {
// 同步处理
if (this.PromiseStatus === FULFILLED) {
onFulfilled(this.PromiseResult)
}
if (this.PromiseStatus === REJECTED) {
onRejected(this.PromiseResult)
}
// 异步处理
if (this.PromiseStatus === PENDING) {
// 订阅
this.onFulfilledCallbacks.push(() => {
// 切片思想, todo something...
onFulfilled(this.PromiseResult)
});
this.onRejectedCallbacks.push(() => {
onRejected(this.PromiseResult)
});
}
}
}
module.exports = Promise;
3. 实现then()方法的链式调用
then()方法的返回值是一个新的Promise对象,也正是因为返回值是一个Promise对象,因此可以实现链式调用。其返回的Promise对象的状态由回调函数如何执行决定:
- 若
onResolved或onRejected回调函数正常执行,且回调函数的返回值是一个Promise对象,则会立即执行该Promise对象,并且将该Promise对象的状态及结果值作为then()方法返回的Promise对象状态及结果; - 若
onResolved或onRejected回调函数正常执行,则返回的Promise对象为成功态,且回调函数的返回值为Promise对象的成功结果; - 若
onResolved或onRejected回调函数抛出异常,则返回的Promise对象为失败态,且抛出的错误为Promise对象的失败原因。
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
// resolvePromise方法用来解析then方法回调的返回值x,并且处理then方法的返回值promise2的状态及结果
const resolvePromise = function (promise2, x, resolve, reject) {
// 1. 防止出现let promise2 = Promise.resolve().then(() => promise2)循环引用死循环,直接抛出错误
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
}
// 2. promise规范规定,promise可以是一个对象或者函数,因此此方法需要兼容所有promise
if (Object.prototype.toString.call(x) === '[object Object]' || typeof x === 'function') {
/* 若x是由外部实现的promise,别人的then方法可能会多次执行会回调或then内部抛出,需要一个标识变量防止resolve、reject多次执行
const x = {
then (onFulfilled, onRejected) {
onFulfilled();
onFulfilled();
onRejected();
throw new Error();
}
}
*/
let called = false;
/* 防止取then属性报错,或执行then时报错,应捕获其错误,一旦抛出异常则立即失败
const x = {};
Object.defineProperty(x, then, {
get () {
throw new Error();
}
})
*/
try {
/* x是别人定义的promise,且可能被defineProperty定义特殊处理过,多次取then报错,为了增加代码的容错性,提前取出保存。
const x = {};
let time = 2;
Object.defineProperty(x, then, {
get () {
if (--time === 0) {
throw new Error();
}
}
})
*/
let then = x.then;
// x有then属性,且值是一个函数,则将其视为一个promise对象
if (typeof then === 'function') {
then.call(x, y => { // 使用call,而不是直接使用x.then(),同样也是防止多次取then报错
// 若已经改变了状态,拒绝继续执行
if (called) return;
called = true;
// 为了防止y还是一个promise,应进行递归处理,如果y不是一个promise,递归后会进入3.直接转为成功
resolvePromise(promise2, y, resolve, reject);
}, e => {
if (called) return;
called = true;
reject(e);
})
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
// 3. x是一个普通值,直接转为成功并将返回值x作为成功结果
resolve(x);
}
}
class Promise {
constructor (executor) {
this.PromiseStatus = PENDING;
this.PromiseResult = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if (this.PromiseStatus === PENDING) {
this.PromiseStatus = FULFILLED;
this.PromiseResult = value;
this.onFulfilledCallbacks.forEach(fn => fn())
}
}
let reject = (reason) => {
if (this.PromiseStatus === PENDING) {
this.PromiseStatus = REJECTED;
this.PromiseResult = reason;
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try {
executor (resolve, reject);
} catch (e) {
reject(e);
}
}
then (onFulfilled, onRejected) {
// then方法返回一个Promise对象
let promise2 = new Promise((resolve, reject) => { // executor同步执行
if (this.PromiseStatus === FULFILLED) {
// 由于executor同步执行,要等待executor执行完毕,new Promise才能返回promise2,因此需要通过异步等待promise2创建成功
process.nextTick(() => { // Promise采用微任务异步等待promise2
// 回调中抛出异常,则then返回一个失败的promise
try {
let x = onFulfilled(this.PromiseResult);
// 解析返回值
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
}
if (this.PromiseStatus === REJECTED) {
process.nextTick (() => {
try {
let x = onRejected(this.PromiseResult)
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
}
if (this.PromiseStatus === PENDING) {
this.onFulfilledCallbacks.push(() => {
process.nextTick (() => {
try {
let x = onFulfilled(this.PromiseResult)
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
});
this.onRejectedCallbacks.push(() => {
process.nextTick (() => {
try {
let x = onRejected(this.PromiseResult)
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
});
}
})
return promise2;
}
}
module.exports = Promise;
4. 实现then()方法值的穿透
class Promise {
then (onFulfilled, onRejected) {
// then方法可选参数处理,如果未传递onFulfilled/onRejected回调函数,则将成功或失败的结果向后传递
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : data => data;
onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error };
}
}
5. 实现deferred延迟方法
实现deferred延迟方法为了符合Promise A+规范,若不存在延迟方法,测试过程中会提示TypeError: adapter.deferred is not a function.错误。该方法在原生javaScirpt中的Promise是没有被实现的。
// 延迟对象,返回一个对象,该对象中保存了一个promise对象及其resolve和reject方法
Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
deferred延迟方法可以用于减少将异步操作封装成Promise对象时回调的嵌套,因此在一些类库中也有实现。
const fs = require('fs');
// 将fs.read方法包装成promise
function read (url) {
return new Promise((resolve, reject) => { // 嵌套
fs.readFile(url, 'utf-8', (err, data) => {
if (err) reject(err);
else resolve(data);
})
})
}
// 实现defer方法
function defer () {
const dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
// 使用延迟对象减少promise嵌套
function read1 (url) {
const dfd = defer();
fs.readFile(url, 'utf-8', (err, data) => {
if (err) dfd.reject(err);
else dfd.resolve(data);
})
return dfd;
}
6. catch()方法的实现
catch()方法是then()方法的语法糖,等同于then(null, onRejected)
class Promise {
catch (onRejected) {
return this.then(null, onRejected);
}
}
7. Promise.all(iterable)方法的实现
Promise.all()是Promise提供的静态方法,接收的参数iterable是一个可迭代对象,常为数组类型,返回值是一个新的Promise对象。
Promise.all()用于处理多个异步并发请求,只有当所有异步都成功,其返回的Promise对象才是成功态,成功的结果组成的数组与iterable中的顺序一致;任何一个异步失败则立即返回失败态的Promise对象。
// 判断是否为promise对象
const isPromise = function (val) {
if (Object.prototype.toString.call(val) === '[object Object]' || typeof val === 'function') {
if (typeof val.then === 'function') return true;
}
return false;
}
class Promise {
static all (iterable) {
return new Promise ((resolve, reject) => {
const res = []; // 存放结果
let count = 0; // 由于存在异步,因此需要计数器统计全部得到值后再改变成功状态
const len = iterable.length;
// 保存成功结果,并计数等待所有异步有结果后将状态置为成功
function saveData(k, v) {
res[k] = v;
if (++count === len) { // 计数
resolve(res)
}
}
for (let i = 0; i < len; i++) {
const val = iterable[i];
// 若是一个promise,则立即执行并等待其状态及结果,若为普通值则立即保存其值
if (isPromise(val)) {
val.then(v => {
saveData(i, v);
}, reject); // 一旦异步失败,则all方法立即失败
} else {
saveData(i, val);
}
}
})
}
}
8. Promise.resolve(value)方法的实现
Promise.resolve()是Promise构造函数的静态方法,返回一个确定状态Promise对象。
Promise.resolve()用于包装普通值时,是手动创建一个fulfilled状态的Promise对象的快捷方法;Promise.resolve()接收Promise对象时,无论是否是嵌套的Promise都将其展开执行,最终返回一个有状态的Promise对象。
但返回的Promise对象最终状态及值取决于传入的参数:
- 若参数时
Promise,则将传入的Promise的执行,并将最终状态及结果值作为返回的Promise对象的状态及结果; - 若为其它值,则为返回的
Promise对象的成功结果。
class Promise {
constuctor (executor) {
let resolve = (value) => {
// 若调用Promise.resolve()方法传入一个promise对象,应执行传入的promise对象,并获取到其值作为Promise.resolve()返回值的状态及结果
if (value instanceof Promise) {
return value.then(resolve, reject); // 递归解析promise
}
if (this.PromiseStatus === PENDING) {
this.PromiseStatus = FULFILLED;
this.PromiseResult = value;
// 发布
this.onFulfilledCallbacks.forEach(fn => fn())
}
}
}
static resolve (value) {
return new Promise ((resolve, reject) => {
resolve(value);
})
}
}
9. Promise.reject(reason)方法的实现
Promise.reject()是Promise构造函数的静态方法,始终返回一个失败的Promise对象,是手动创建一个rejected状态的Promise对象的快捷方法。
class Promise {
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}
}
10. Promise.race(iterable)方法的实现
race()方法是Promise提供的静态方法,其返回值是一个Promise对象。其状态和结果取决于iterable中第一个确定状态结果的Promise对象。
class Promise {
static race (iterable) {
return new Promise((resolve, reject) => {
iterable.forEach((value) => {
// 最先确定状态的promise对象决定race方法返回的promise对象的状态及结果
Promise.resolve(value).then(resolve, reject);
});
});
}
}
11. Promie.prototype.allSettled(iterable)方法的实现
无论iterable中是否存在失败的Promise,Promise.allSettled()方法始终后返回一个成功的Promise对象。其成功的结果为一个数组,数组中的元素是一个对象,每个对象都包含以下属性:
status:表示该下标的Promise对象的最终状态,值为fulfilled或rejected。value:当状态为fulfilled时,才存在该属性,其值为成功的结果。reason:当状态为rejected时,才存在该属性,其值为失败的原因。
class Promsie {
static allSettled (iterable) {
return new Promise ((resolve) => {
const data = [];
const list = [...iterable];
let len = list.length;
list.forEach((item, index) => {
Promise.resolve(item).then((v) => {
// 保存成功的结果
data[index] = {
status: 'fulfilled',
value: v
}
// 计数,当所有的promise都有结果后将返回的promise对象置为成功态
if (--len === 0) {
resolve(data);
}
}, e => {
// 保存失败的结果
data[index] = {
status: 'rejected',
reason: e
}
if (--len === 0) {
resolve(data);
}
})
})
})
}
}
12. Promise.prototype.finally(onFinally)方法的实现
当一个Promise的状态确定后,无论是fulfilled还是rejected,onFinally回调都会执行。
finally()方法返回一个新的Promise对象,若onFinally回调中抛出错误或返回一个失败的Promise,则finally()方法返回的Promise对象的状态为rejected及其原因,否则finally()方法返回新Promise对象与当前执行finally()方法的Promise对象状态保持一致。
class Promise {
finally (onFinally) {
// 无论成功或失败onFinally都会执行, 因此在then的成功和失败中都执行
return this.then (data => {
// onFinally可能会返回一个promise对象,因此可以使用Promise.resolve(),来解析onFinally的返回值
// 如果返回失败的promise或抛出错误,则采取onFinally的失败及其原因;除此以外onFinally不会影响原先的值的状态(即采用之前的状态和值)
Promise.resolve(onFinally()).then(() => value, e => { throw e });
}, err => {
// 基于当前Promise对象的失败状态或onFinally返回的失败状态
return Promise.resolve(onFinally()).then(() => { throw err }, e => { throw e });
})
}
}