promise
术语
- thenable 是具有then方法的一个对象
- promise 是一个thenable并且遵循PromiseA+规范。代表一个异步操作的最终结果。
- value 是promise状态成功(fulfilled)时的结果值,也就是我们调用resolve给到的值。
- reason 是promise状态失败(rejected)时的结果值,也就是我们调用reject给到的值。
状态
- pending 待处理状态,代表promise执行还未完成。
- fulfilled 成功状态,代表promise执行成功,也可以说是resolve调用后的状态。
- rejected 失败状态,代表promise执行失败,也可以说是reject调用后的状态。
promise默认是pending状态也就是带处理状态,切promise只能从peding状态转换到fulfilled状态或rejected状态,且promise只能改变一次状态。fulfilled和rejected两个状态不能相互转换。
实现
因为Promise实际是微任务运行,当时浏览器只能通过Promise才能执行微任务,所以我们将以宏任务的方式实现,但整体的promise执行时序还是遵循promise。
状态定义
/**
* 待处理状态
* @type {string}
* @readonly
*/
const PENDING = "pending";
/**
* 成功状态
* @type {string}
* @readonly
*/
const FULFILLED = "fulfilled";
/**
* 失败状态
* @type {string}
* @readonly
*/
const REJECTED = "rejected";
Promise的类结构
/**
* @class
*/
class Promise {
/**
* @param {Function} Promise待执行任务
* @constructor
*/
constructor(fn) {
/**
* Promise的当前状态
* @type {string}
*/
this.status = PENDING;
/**
* Promise成功时的结果值
* @type {any}
*/
this.value = null;
/**
* Promise失败时的结果值
* @type {any}
*/
this.reason = null;
/**
* 待执行的成功回调(只有状态为fulfilled时才会执行)
* 至于为什么是一个数组呢?因为通一个promise可以调用多次then方法
* @type {Function[]}
*/
this.onFulfilledCallbacks = [];
/**
* 待执行的失败回调(只有状态为rejected时才会执行)
* 数组的原因和onFulfilledCallbacks同理
* @type {Function[]}
*/
this.onRejectedCallbacks = [];
// 成功回调
const resolve = (value) => {
...
};
// 失败回调
const reject = (reason) => {
...
};
// 执行Promise任务
try {
fn(resolve, reject);
} catch(e) {
reject(e);
}
}
/**
* 用来处理promise下一步的逻辑,会根据promise状态执行对应的回调函数
* @method
* @param {Function} onFulfilled promise成功时调用的回调
* @param {Function} onRejected promise失败时调用的回调
* @return {Promise}
*/
then(onFulfilled, onRejected) {
...
}
}
resolve和reject的实现以及和then方法的关系
then方法是用来在promies执行完成后获取执行结果,再基于执行结果调用相应的回调函数的方法。并且then方法会返回一个全新的Promise。
问:then方法返回的全新的Promise的状态是基于什么来转换的呢?
答:then方法返回的Promise是基于当前Promise的状态来转换的。如果当前Promise调用的resolve那新创建的Promise也是成功状态,反之亦然。
class Promise {
constructor() {
...
const resolve = (value) => {
// 如果resolve的是一个Promise,则需要等待promise执行完成,获取promise的结果返回
if (value instanceof Promise) {
value.then(resolve, reject);
return;
}
setTimeout(() => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
for (let callback of this.onFulfilledCallbacks) {
callback(value);
}
}
});
};
const reject = (reason) => {
// reject中如果reason是一个promise,就将promise当作错误原因返回
setTimeout(() => {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
for (let callback of this.onRejectedCallbacks) {
callback(reason);
}
}
});
};
...
}
then(onFulfilled, onRejetect) {
onFulfilled = typeof onFulfilled !== "function" ? (value) => value : onFulfilled;
onRejected = typeof onRejected !== "function" ? (err) => { throw err } : onRejected;
// then方法需要返回一个全新的promise,且全新的promise的状态是依赖当前promise来的
let promise = new Promise((resolve, reject) => {
const onFulfilledCallback = (value) => {
// 用try...catch的原因。因为then方法中给到的代码是用户编写的,所以可能导致出现异常。如果出现异常新创建的promise转换为失败状态
try {
executor(
promise,
onFulfilled(value),
resolve,
reject
);
} catch (e) {
reject(e);
}
};
const onRejectedCallback = (reason) => {
try {
executor(
promise,
onRejected(reason),
resolve,
reject
);
} catch (e) {
reject(e);
}
};
if (this.status === PENDING) {
// 如果当前promise状态是pending,就将其添加到对应的数组,用于等待promise结束后,执行对应的回调
this.onFulfilledCallbacks.push(onFulfilledCallback);
this.onRejectedCallbacks.push(onRejectedCallback);
} else if (this.status === FULFILLED) {
setTimeout(() => {
onFulfilledCallback(this.value);
});
} else if (this.status === REJECTED) {
setTimeout(() => {
onRejectedCallback(this.reason);
});
}
});
return promise;
}
}
module.exports = Promise;
executor实现
then方法中我们调用了executor这个函数,这个函数是干什么的?
是用来处理回调函数返回结果的。用来处理一些特殊情况。
例:嵌套Promise
let promise1 = new Promise((resolve, reject) => {
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
});
})
resolve(promise2);
});
这个示例中最终promise1的结果值是2,印务resolve的逻辑通过exector等待了嵌套了的promise2。
例:循环Promise
let promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(promise3);
});
});
结果是抛出一个异常。
Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
exetor函数的目的就是为了解决类似这样的问题
/**
* @param {Promise} promise 新创建的Promise
* @param {any} x 成功或失败回调函数的返回值
* @param {Function} resolve 新建Promise的resolve
* @param {Function} reject 新建Promise的reject
*/
function executor(promise, x, resolve, reject) {
// 如果是循环引用就需要抛出异常
if (promise === x) {
throw new TypeError("Chaining cycle detected for promise #<Promise>");
}
// 只允许调用一次,因为不能确定第三方实现是否会多次调用,所以需要标识一下代表执行过。
let called = false;
// 如果返回值是一个Promise,就等待返回的Promise的结果。
if (x instanceof Promise) {
x.then(
(y) => {
// 递归处理结果
executor(promise, y, resolve, reject);
},
(err) => {
reject(err);
}
);
} else if (x && (typeof x === "object" || typeof x === "function")) {
try {
var then = x.then;
if (typeof then === "function") {
// 如果返回结果是一个thenable可能是其他实现的Promise,尝试调用其他实现的then来等待结果
then.call(
x,
(y) => {
if (called) {
return;
}
called = true;
executor(promise, y, resolve, reject);
},
(err) => {
if (called) {
return;
}
called = true;
reject(err);
}
);
} else {
// 如果返回结果是一个非thenable,就当作一个结果值
resolve(x);
}
} catch (e) {
// 如果其他实现出现异常就转换微失败状态
if (called) {
return;
}
called = true;
reject(e);
}
} else {
// 不是对象也不是Promise那就是一些基本数据类型,当作结果值返回
resolve(x);
}
}
Promise基础功能的完整实现
/**
* 待处理状态
* @type {string}
* @readonly
*/
const PENDING = "pending";
/**
* 成功状态
* @type {string}
* @readonly
*/
const FULFILLED = "fulfilled";
/**
* 失败状态
* @type {string}
* @readonly
*/
const REJECTED = "rejected";
/**
* @param {Promise} promise 新创建的Promise
* @param {any} x 成功或失败回调函数的返回值
* @param {Function} resolve 新建Promise的resolve
* @param {Function} reject 新建Promise的reject
*/
function executor(promise, x, resolve, reject) {
// 如果是循环引用就需要抛出异常
if (promise === x) {
throw new TypeError("Chaining cycle detected for promise #<Promise>");
}
// 只允许调用一次,因为不能确定第三方实现是否会多次调用,所以需要标识一下代表执行过。
let called = false;
// 如果返回值是一个Promise,就等待返回的Promise的结果。
if (x instanceof Promise) {
x.then(
(y) => {
// 递归处理结果
executor(promise, y, resolve, reject);
},
(err) => {
reject(err);
}
);
} else if (x && (typeof x === "object" || typeof x === "function")) {
try {
var then = x.then;
if (typeof then === "function") {
// 如果返回结果是一个thenable可能是其他实现的Promise,尝试调用其他实现的then来等待结果
then.call(
x,
(y) => {
if (called) {
return;
}
called = true;
executor(promise, y, resolve, reject);
},
(err) => {
if (called) {
return;
}
called = true;
reject(err);
}
);
} else {
// 如果返回结果是一个非thenable,就当作一个结果值
resolve(x);
}
} catch (e) {
// 如果其他实现出现异常就转换微失败状态
if (called) {
return;
}
called = true;
reject(e);
}
} else {
// 不是对象也不是Promise那就是一些基本数据类型,当作结果值返回
resolve(x);
}
}
/**
* @class
*/
class Promise {
/**
* @param {Function} Promise待执行任务
* @constructor
*/
constructor(fn) {
/**
* Promise的当前状态
* @type {string}
*/
this.status = PENDING;
/**
* Promise成功时的结果值
* @type {any}
*/
this.value = null;
/**
* Promise失败时的结果值
* @type {any}
*/
this.reason = null;
/**
* 待执行的成功回调(只有状态为fulfilled时才会执行)
* 至于为什么是一个数组呢?因为通一个promise可以调用多次then方法
* @type {Function[]}
*/
this.onFulfilledCallbacks = [];
/**
* 待执行的失败回调(只有状态为rejected时才会执行)
* 数组的原因和onFulfilledCallbacks同理
* @type {Function[]}
*/
this.onRejectedCallbacks = [];
// 成功回调
const resolve = (value) => {
// 如果resolve的是一个Promise,则需要等待promise执行完成,获取promise的结果返回
if (value instanceof Promise) {
value.then(resolve, reject);
return;
}
setTimeout(() => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
for (let callback of this.onFulfilledCallbacks) {
callback(value);
}
}
});
};
// 失败回调
const reject = (reason) => {
// reject中如果reason是一个promise,就将promise当作错误原因返回
setTimeout(() => {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
for (let callback of this.onRejectedCallbacks) {
callback(reason);
}
}
});
};
// 执行Promise任务
try {
fn(resolve, reject);
} catch(e) {
reject(e);
}
}
/**
* 用来处理promise下一步的逻辑,会根据promise状态执行对应的回调函数
* @method
* @param {Function} onFulfilled promise成功时调用的回调
* @param {Function} onRejected promise失败时调用的回调
* @return {Promise}
*/
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled !== "function" ? (value) => value : onFulfilled;
onRejected = typeof onRejected !== "function" ? (err) => { throw err } : onRejected;
// then方法需要返回一个全新的promise,且全新的promise的状态是依赖当前promise来的
let promise = new Promise((resolve, reject) => {
const onFulfilledCallback = (value) => {
// 用try...catch的原因。因为then方法中给到的代码是用户编写的,所以可能导致出现异常。如果出现异常新创建的promise转换为失败状态
try {
executor(
promise,
onFulfilled(value),
resolve,
reject
);
} catch (e) {
reject(e);
}
};
const onRejectedCallback = (reason) => {
try {
executor(
promise,
onRejected(reason),
resolve,
reject
);
} catch (e) {
reject(e);
}
};
if (this.status === PENDING) {
// 如果当前promise状态是pending,就将其添加到对应的数组,用于等待promise结束后,执行对应的回调
this.onFulfilledCallbacks.push(onFulfilledCallback);
this.onRejectedCallbacks.push(onRejectedCallback);
} else if (this.status === FULFILLED) {
setTimeout(() => {
onFulfilledCallback(this.value);
});
} else if (this.status === REJECTED) {
setTimeout(() => {
onRejectedCallback(this.reason);
});
}
});
return promise;
}
}
module.exports = Promise;
扩展功能
deferred函数实现
有时候Promise的等待结果可能不是在传入的函数中就能够实现的,比如一个模态窗口的确认和取消。
这种情况我们可以将Promise中的resolve和reject函数放到外部来。让后其他地方控制Promise是否完成。这种功能也叫做deffered函数所实现的功能。
class Promise {
...
/**
* 让外部转换Promise的状态
* @static
* @method
* @return {Promise & { resolve: Function, reject: Function }}
*/
static deferred() {
let resolve = null;
let reject = null;
let promise = new Promise((rev, rej) => {
resolve = rev;
reject = rej;
});
promise.resolve = resolve;
promise.reject = reject;
promise.promise = promise;
return promise;
}
}
单元测试
单元测试我们通过 promises-aplus-tests 这个npm包来进行单元测试
安装promises-aplus-tests
yarn add promises-aplus-tests
写下测试脚本到package.json
{
"scripts": {
"test": "promises-aplus-tests ./promise.js"
}
}
运行测试
yarn run test
运行结果最终如下就代表成功了
...
✓ eventually-rejected
The value is `1` with `Number.prototype` modified to have a `then` method
✓ already-fulfilled
✓ immediately-fulfilled
✓ eventually-fulfilled
✓ already-rejected
✓ immediately-rejected
✓ eventually-rejected
872 passing (16s)
参考
[1] Promise A+规范