共计 11692 个字符,预计需要花费 30 分钟才能阅读完成。
github
博客
Promise
的申明
当咱们应用 Promise
的时候,通常都是new Promise((resolve, reject) => {})
。
因而咱们能够看出:
Promise
是一个类;Promise
类的构造函数的第一个参数是函数,这个函数叫处理器函数(executor function
);-
而在处理器函数中,有了两个参数:
resolve
和reject
- 当异步工作顺利完成且返回后果值的时候,咱们会调用 `resolve` 函数;- 当异步工作失败且返回失败起因(通常是一个谬误对象)时,会调用 `reject` 函数。
因而,咱们能够初步申明一下 Promise
类。
class Promise {
/**
* 结构器
* @returns {Promise<object>}
* @param executor<function>: executor 有两个参数:resolve 和 reject
*/
constructor(executor) {
// resolve 胜利
const resolve = () => {};
// reject 失败
const reject = () => {};
// 执行 executor
executor(resolve,reject);
}
}
实现 Promise 的根本状态
Promise
存在着三种状态:pending
(期待态)、fulfilled
(胜利态)和rejected
(失败态):
Promise
的初始状态是pending
状态;pending
状态能够转换为fulfilled
状态和rejected
状态;fulfilled
状态不能够转为其余状态,且必须有一个不可扭转的值(value);rejected
状态不能够转为其余状态,且必须有一个不可扭转的起因(reason);- 当在处理器函数中调用
resolve
函数并传入参数 value,则状态扭转为fulfilled
,且不能够扭转; - 当在处理器函数中调用
reject
函数并传入参数 reason,则状态扭转为rejected
,且不能够扭转; - 若处理器函数执行中报错,间接执行
reject
函数。
因而,咱们须要在 Promise
类中设置三个变量:state
(状态变量),value
(胜利值的变量)和 reason
(失败起因的变量),而后在resolve
函数、reject
函数以及执行 executor
函数报错的时候扭转 state
的值。
class Promise {constructor(executor) {
// 初始化状态
this.state = 'pending';
// 胜利的值
this.value = undefined;
// 失败的起因
this.reason = undefined;
/**
* resolve 胜利函数
* @param value<any>: 胜利的值
*/
const resolve = (value) => {
// 只能在状态为 pending 的时候执行
if(this.state === 'pending'){
// resolve 调用后,state 转化为 fulfilled
this.state = 'fulfilled';
// 存储 value
this.value = value;
}
};
/**
* reject 失败函数
* @param reason<any>: 失败的起因
*/
const reject = (reason) => {
// 只能在状态为 pending 的时候执行
if(this.state === 'pending'){
// resolve 调用后,state 转化为 rejected
this.state = 'rejected';
// 存储 reason
this.reason = reason;
}
};
// 如果 executor 执行报错,间接执行 reject()
try {executor(resolve,reject);
}catch (e){reject(e);
}
}
}
then
办法
Promise
有一个 then
办法,而该办法中有两个参数:onFulfilled
和onRejected
:
- 这两个参数都是一个函数,且会返回一个后果值;
- 当状态为
fulfilled
,只执行onFulfilled
,传入this.value
; - 当状态为
rejected
,只执行onRejected
,传入this.reason
;
因而咱们能够来实现一下 then
办法。
class Promise {constructor(executor) {...}
/**
* then 办法
* @param onFulfilled<function>: 状态为 fulfilled 时调用
* @param onRejected<function>: 状态为 rejected 时调用
*/
then(onFulfilled, onRejected) {
// 状态为 fulfilled 的时候,执行 onFulfilled,并传入 this.value
if(this.state === 'fulfilled'){
/**
* onFulfilled 办法
* @param value<function>: 胜利的后果
*/
onFulfilled(this.value)
}
// 状态为 rejected 的时候,onRejected,并传入 this.reason
if(this.state === 'rejected'){
/**
* onRejected 办法
* @param reason<function>: 失败的起因
*/
onRejected(this.reason)
}
}
}
异步实现
Promise
实际上一个异步操作:
resolve()
是在setTimeout
内执行的;- 当执行
then()
函数时,如果状态是pending
时,咱们须要期待状态完结后,才继续执行,因而此时咱们须要将then()
的两个参数onFulfilled
和onRejected
存起来; - 因为一个
Promise
实例能够调用屡次then()
,因而咱们须要将onFulfilled
和onRejected
各种用数组存起来。
因而咱们能够借着欠缺代码:
class Promise {
/**
* 结构器
* @returns {Promise<object>}
* @param executor<function>: executor 有两个参数:resolve 和 reject
*/
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
// 存储 onFulfilled 的数组
this.onResolvedCallbacks = [];
// 存储 onRejected 的数组
this.onRejectedCallbacks = [];
const resolve = (value) => {if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
// 一旦 resolve 执行,调用 onResolvedCallbacks 数组的函数
this.onResolvedCallbacks.forEach(fn => fn());
}
};
const reject = (reason) => {if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
// 一旦 reject 执行,调用 onRejectedCallbacks 数组的函数
this.onRejectedCallbacks.forEach(fn=>fn());
}
};
try {executor(resolve, reject);
} catch (e) {reject(e);
}
}
then(onFulfilled, onRejected) {if (this.state === 'fulfilled') {onFulfilled(this.value)
}
if (this.state === 'rejected') {onRejected(this.reason)
}
// 状态为 pending 的时候,将 onFulfilled、onRejected 存入数组
if (this.state === 'pending') {this.onResolvedCallbacks.push(() => {onFulfilled(this.value)
})
this.onRejectedCallbacks.push(() => {onRejected(this.reason)
})
}
}
}
实现链式调用
咱们经常会像上面代码一样应用Promise
:
new Promise()
.then()
.then()
.then()
这种办法叫做 链式调用,通常是用来解决回调天堂(Callback Hell
)的,就如下的代码:
fs.readdir(source, function (err, files) {if (err) {console.log('Error finding files:' + err)
} else {files.forEach(function (filename, fileIndex) {console.log(filename)
gm(source + filename).size(function (err, values) {if (err) {console.log('Error identifying file size:' + err)
} else {console.log(filename + ':' + values)
aspect = (values.width / values.height)
widths.forEach(function (width, widthIndex) {height = Math.round(width / aspect)
console.log('resizing' + filename + 'to' + height + 'x' + height)
this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {if (err) console.log('Error writing file:' + err)
})
}.bind(this))
}
})
})
}
})
为了实现链式调用,咱们须要满足一下几点:
- 咱们须要在
then()
返回一个新的Promise
实例; - 如果上一个
then()
返回了一个值,则这个值就是onFulfilled()
或者onRejected()
的值,咱们须要把这个值传递到下一个then()
中。
而对于上一个 then()
的返回值,咱们须要对齐进行肯定的解决,因而封装一个 resolvePromise()
的办法去进行判断解决;
接下来咱们对 then()
办法进行批改:
class Promise {constructor(executor) {...}
/**
* then 办法
* @returns {Promise<object>}
* @param onFulfilled<function>: 状态为 fulfilled 时调用
* @param onRejected<function>: 状态为 rejected 时调用
*/
then(onFulfilled, onRejected) {
// 返回一个新的 Promise 实例
const newPromise = new Promise((resolve, reject) => {if (this.state === 'fulfilled') {const x = onFulfilled(this.value)
// 对返回值进行解决
resolvePromise(newPromise, x, resolve, reject);
}
if (this.state === 'rejected') {const x = onRejected(this.reason);
// 对返回值进行解决
resolvePromise(x, resolve, reject);
}
if (this.state === 'pending') {this.onResolvedCallbacks.push(() => {const x = onFulfilled(this.value);
// 对返回值进行解决
resolvePromise(newPromise, x, resolve, reject);
})
this.onRejectedCallbacks.push(() => {const x = onRejected(this.reason);
// 对返回值进行解决
resolvePromise(newPromise, x, resolve, reject);
})
}
});
return newPromise;
}
}
function resolvePromise() {}
实现 resolvePromise
函数
对于上一个 then()
的返回值,咱们用 x
变量存起来,而后须要对它进行一个解决:
-
判断
x
是不是Promise
实例;- 如果是
Promise
实例,则取它的后果,作为新的Promise
实例胜利的后果; - 如果是一般值,间接作为
Promise
胜利的后果;
- 如果是
而后咱们解决返回值后,须要利用 newPromise
的resolve
和 reject
办法将后果返回。
这里咱们还须要留神一个中央,就是 x
等于 newPromise
的话,这时会造成循环援用,导致死循环。
let p = new Promise(resolve => {resolve(0);
});
const p2 = p.then(data => {
// 循环援用,本人期待本人实现,导致死循环
return p2;
})
因而,resolvePromise
函数须要 4 个参数,即 newPromise
,x
、resolve
和reject
。
所以咱们来实现一下 resolvePromise
函数:
/**
* resolvePromise 办法
* @param newPromise<object>: 新的 Promise 实例
* @param x<any>: 上一个 then()的返回值
* @param resolve<function>:Promise 实例的 resolve 办法
* @param reject<function>:Promise 实例的 reject 办法
*/
function resolvePromise(newPromise, x, resolve, reject) {
// 循环援用报错
if(x === newPromise){
// reject 报错
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 避免屡次调用
let called;
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then;
// x 为 Promise 实例
if (typeof then === 'function') {// 应用 call 执行 then(),call 的第一个参数是 this,后续即 then()的参数,即第二个是胜利的回调办法,第三个为失败的回调函数
then.call(x, y => {
// 胜利和失败只能调用一个
if(called)return;
called = true;
// resolve 的后果仍旧是 promise 实例,那就持续解析
resolvePromise(newPromise, y, resolve, reject);
}, err => {
// 胜利和失败只能调用一个
if(called)return;
called = true;
// 失败了就间接返回 reject 报错
reject(err);
})
} else {
// x 为一般的对象或办法,间接返回
resolve(x);
}
} catch (e) {if(called)return;
called = true;
reject(e);
}
} else {
// x 为一般的值,间接返回
resolve(x);
}
}
onFulfilled
和onRejected
对于 then()
的两个参数——onFulfilled
和onRejected
:
-
它们都是可选参数,而且它们都是函数,如果不是函数的话,就会被疏忽掉;
- 如果
onFulfilled
不是一个函数,就将它间接替换成函数value => value
; - 如果
onRejected
不是一个函数,就将它间接替换成函数err => {throw err}
;
- 如果
class Promise {constructor(executor) {...}
then(onFulfilled, onRejected) {
// onFulfilled 如果不是函数,就疏忽 onFulfilled,间接返回 value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
// onRejected 如果不是函数,就疏忽 onRejected,间接抛出谬误
onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err};
...
}
}
其次,onFulfilled
和 onRejected
是不能同步被调用的,必须异步调用。因而咱们就用 setTimeout
解决一步问题。
class Promise {constructor(executor) {...}
then(onFulfilled, onRejected) {
// onFulfilled 如果不是函数,就疏忽 onFulfilled,间接返回 value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
// onRejected 如果不是函数,就疏忽 onRejected,间接抛出谬误
onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err};
return new Promise((resolve, reject) => {if (this.state === 'fulfilled') {
// 异步调用
setTimeout(() => {
try {const x = onFulfilled(this.value)
resolvePromise(x, resolve, reject);
}catch (e){reject(e)
}
})
}
if (this.state === 'rejected') {
// 异步调用
setTimeout(() => {
try{const x = onRejected(this.reason);
resolvePromise(x, resolve, reject);
}catch (e){reject(e)
}
})
}
if (this.state === 'pending') {this.onResolvedCallbacks.push(() => {
// 异步调用
setTimeout(() => {
try {const x = onFulfilled(this.value);
resolvePromise(x, resolve, reject);
}catch (e){reject(e)
}
})
})
this.onRejectedCallbacks.push(() => {
// 异步调用
setTimeout(() => {
try {const x = onRejected(this.reason);
resolvePromise(x, resolve, reject);
}catch (e){reject(e)
}
})
})
}
});
}
}
实现 Promise
的其余办法
Promise.all()
Promise.all()
办法接管一个 promise
的iterable
类型的输出,包含 Array
、Map
、Set
。而后返回一个Promise
实例,该实例回调返回的后果是一个数组,蕴含输出所有 promise
的回调后果。
但只有任何一个输出的 promise
的reject
回调执行或者输出不非法的promise
,就会立马抛出谬误。
/**
* Promise.all 办法
* @returns {Promise<object>}
* @param promises<iterable>: 一个 promise 的 iterable 类型输出
*/
Promise.all = function (promises) {let arr = [];
return new Promise((resolve, reject) => {if (!promises.length) resolve([]);
// 遍历 promises
for(const promise of promises) {
promise.then(res => {arr.push(res);
if(arr.length === promises.length){resolve(arr);
}
}, reject)
}
})
}
Promise.allSettled()
Promise.allSettled()
其实跟 Promise.all()
很像,同样是接管一个 promise
的iterable
类型的输出,但返回的是一个给定的 promise
曾经实现后的 promise
,并带有一个对象数组,每个对象标识着对应的promise
后果。
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
Promise.allSettled(promises).
then((results) => console.log(results));
// > Array [Object { status: "fulfilled", value: 3}, Object {status: "rejected", reason: "foo"}]
实现:
/**
* Promise.allSettled 办法
* @returns {Promise<object>}
* @param promises<iterable>: 一个 promise 的 iterable 类型输出
*/
Promise.allSettled = function (promises) {let arr = [];
return new Promise((resolve, reject) => {
try {const processData = (data) => {arr.push(data);
if(arr.length === promises.length){resolve(arr);
}
}
if (!promises.length) resolve([]);
// 遍历 promises
for(const promise of promises) {
promise.then(res => {processData({state:'fulfilled', value: res})
}, err => {processData({state:'rejected', reason: err})
})
}
}catch (e){reject(e)
}
})
}
Promise.any()
Promise.any()
跟 Promise.all()
和Promise.allSettled()一样,同样是接管一个
promise 的
iterable类型的输出。但只有其中的一个
promise 胜利,就返回那个曾经胜利的
promise,但如果没有一个
promise 胜利,就返回一个失败的
promise`。
/**
* Promise.any 办法
* @returns {Promise<object>}
* @param promises<iterable>: 一个 promise 的 iterable 类型输出
*/
Promise.any = function (promises) {return new Promise((resolve, reject) => {
// 如果传入的参数是一个空的可迭代对象,则返回一个 已失败(already rejected)状态的 Promise
if (!promises.length) reject();
// 如果传入的参数不蕴含任何 promise,则返回一个 异步实现(asynchronously resolved)的 Promise。if (typeof promises[Symbol.iterator] !== 'function' ||
promises === null ||
typeof promises === 'string') {resolve()
}
let i = 0;
// 遍历 promises
for (const promise of promises) {
promise.then(res => {
i++;
resolve(res);
}, err => {
i++;
if (i === promises.length) {reject(err);
}
})
}
})
}
Promise.race()
Promise.race()
,同样是接管一个 promise
的iterable
类型的输出。一旦迭代器中的某个 promise
实现了,不论是胜利还是失败,就会返回这个promise
。
/**
* Promise.race 办法
* @returns {Promise<object>}
* @param promises<iterable>: 一个 promise 的 iterable 类型输出
*/
Promise.race = function (promises) {return new Promise((resolve, reject) => {for (const promise of promises) {promise.then(resolve, reject)
}
})
}
Promise.reject()
和Promise.resolve()
Promise.reject()
办法返回一个带有回绝起因的 Promise
对象;Promise.resolve()
办法返回一个以定值解析后的 Promise
对象。
/**
* Promise.reject 办法
* @returns {Promise<object>}
* @param val<any>
*/
Promise.reject = function (val) {return new Promise(reject => reject(val))
}
/**
* Promise.resolve 办法
* @returns {Promise<object>}
* @param val<any>
*/
Promise.resolve = function (val) {return new Promise(resolve => resolve(val))
}
catch()
和finally()
catch()
办法是用来解决失败的状况,它传入一个处理函数,而后返回一个 promise
实例。实际上它是 then()
的语法糖,只承受 rejected
态的数据。
finally()
是在 promise
完结时,无论后果是 fufilled
还是 rejected
,都会执行指定的回调函数。同样也返回一个promise
实例。
class Promise {constructor(executor) {...}
then(onFulfilled, onRejected) {...}
/**
* catch 办法
* @returns {Promise<object>}
* @param callback<function>: 处理函数
*/
catch(callback) {return this.then(null, callback);
}
/**
* finally 办法
* @returns {Promise<object>}
* @param callback<function>: 处理函数
*/
finally(callback) {
return this.then(res => {return Promise.resolve(callback()).then(() => res)
}, err => {return Promise.reject(callback()).then(() => {throw err})
})
}
}
Promise/A+ 测试
Promise/A+
标准: https://github.com/promises-a…
Promise/A+
测试工具: https://github.com/promises-a…
装置 promises-aplus-tests
插件。
yarn add promises-aplus-tests
在 Promise.js
前面插入下列代码。
// 测试
Promise.defer = Promise.deferred = function () {let dfd = {}
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
module.exports = Promise;
而后输出命令行进行测试。
promises-aplus-tests Promise.js
后果:
872 passing (18s)