目录
Promises/A+规范
术语:
要求: Promise状态:
Then 方法
参数可选
onFulfilled 特性
onRejected 特性
调用时机
调用要求
多次调用
返回
Promise解决过程
注释:
promise/a+规范测试
完整代码
一个开放的标准,用于实现者之间的可互操作的JavaScript Promise。
Promise代表异步操作的最终结果。与Promise交互的主要方式是通过其then方法,该方法注册回调函数以接收Promise的最终值或无法实现Promise的原因。
本规范详细说明了then方法的行为,提供了一个可互操作的基础,所有符合Promises/A+规范的Promise实现都可以依赖该基础。因此,该规范应被认为是非常稳定的。尽管Promises/A+组织可能会偶尔修订该规范以解决新发现的边缘情况,但我们只会在经过慎重考虑、讨论和测试后才会集成大的或不兼容的变更。
从历史上看,Promises/A+澄清了早期Promises/A提案的行为条款,扩展了其覆盖的事实行为,并省略了未明确规定或存在问题的部分。
最后,核心的Promises/A+规范不涉及如何创建、实现或拒绝Promise,而是选择专注于提供可互操作的then方法。在伴随规范的未来工作中可能会涉及这些主题
这段话描述了Promise的then方法的规范。Promise是一种用于处理异步操作的对象,then方法用于注册回调函数,以便在Promise被履行(fulfilled)或被拒绝(rejected)时执行相应的操作。then方法接受两个参数,分别是在Promise被履行时调用的回调函数onFulfilled和在Promise被拒绝时调用的回调函数onRejected。这两个回调函数都是可选的,如果不提供,则会被忽略。
根据规范,当Promise被履行时,会按照注册的顺序执行所有的onFulfilled回调函数;当Promise被拒绝时,会按照注册的顺序执行所有的onRejected回调函数。同时,规范还定义了一些其他的行为,比如then方法可以多次调用同一个Promise,如果回调函数返回一个值,则会运行Promise解析过程来处理返回值,如果回调函数抛出异常,则Promise会被拒绝。
一个 promise 必须提供一个 then
方法以访问其当前值、终值和据因。
promise 的 then
方法接受两个参数:
promise.then(onFulfilled, onRejected)
onFulfilled
和 onRejected
都是可选参数。
onFulfilled
不是函数,其必须被忽略onRejected
不是函数,其必须被忽略onFulfilled
特性如果 onFulfilled
是函数:
onRejected
特性如果 onRejected
是函数:
onFulfilled
和 onRejected
只有在执行环境堆栈仅包含平台代码时才可被调用
onFulfilled
和 onRejected
必须被作为函数调用(即没有 this
值)
then
方法可以被同一个 promise
调用多次
then
方法必须返回一个 promise
对象
promise2 = promise1.then(onFulfilled, onRejected);
onFulfilled
或者 onRejected
返回一个值 x
,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)
简单来说,当一个Promise对象的成功回调或失败回调返回一个值时,这个值将被传递给Promise解决过程的[[Resolve]]步骤。这个步骤会根据这个值的类型和状态,决定如何处理这个Promise对象,可能会将其标记为已解决(fulfilled)或已拒绝(rejected),并执行相应的回调函数。
这个过程的目的是确保Promise对象能够正确地处理成功和失败的情况,并按照规范的要求进行相应的处理。
Promise解决过程是一个抽象操作,接受一个Promise和一个值作为输入,我们将其表示为[[Resolve]](promise, x)。如果x是一个thenable对象,它会尝试使promise采用x的状态,假设x至少有一些类似Promise的行为。否则,它会用值x来履行promise。
这种对thenable对象的处理允许Promise实现进行互操作,只要它们公开符合Promises/A+规范的then方法。它还允许Promises/A+实现通过合理的then方法来“吸收”不符合规范的实现。
要运行[[Resolve]](promise, x),执行以下步骤:
这段话是关于一个测试套件的说明。这个套件用于测试一个Promise实现是否符合Promises/A+规范。
通过在这个仓库中通过测试,意味着你的实现具有符合Promises/A+规范的then()方法,并且你可以在你的README中展示Promises/A+的标志。你还可以发送一个pull request,将你的实现列在实现页面上。
如何运行 这些测试可以在Node.js环境或者正确设置的浏览器环境中运行。
适配器 为了测试你的Promise库,你需要暴露一个非常简单的适配器接口。这些适配器以Node.js模块的形式编写,具有一些众所周知的导出项:
resolved和rejected导出项实际上是可选的,如果它们不存在,测试运行器将使用deferred自动创建它们。但是,如果你的Promise库具有创建已解决或已拒绝Promise的能力,那么你应该包含这些导出项,以便测试运行器可以提供更好的代码覆盖率,并发现这些方法中的任何错误。
请注意,测试将永远不会将Promise或thenable作为解决值传递。这意味着我们从不直接使用接受Promise或thenable的resolve操作形式,而只使用直接的fulfillment操作,因为在没有给定thenable时,fulfill和resolve是等效的。
最后,请注意,包括deferred().resolve和deferred().reject在内的所有这些函数都不应该抛出异常。测试的结构无法处理异常,如果你的实现有可能抛出异常,比如在尝试解决一个已解决的Promise时抛出异常,你应该在编写适配器时使用try/catch来包装对你的实现的直接调用。
通过命令行界面 这个包带有一个命令行界面,可以通过全局安装
npm install promises-aplus-tests -g
或者将其包含在你的package.json的devDependencies中,并使用npm的scripts功能。在后一种情况下,你的设置可能如下所示:
{
"devDependencies": {
"promises-aplus-tests": "*"
},
"scripts": {
"test": "run-my-own-tests && promises-aplus-tests test/my-adapter"
}
}
命令行界面的第一个参数是适配器文件的文件名,相对于当前工作目录。它会尝试将后续的选项传递给Mocha,所以你可以使用例如--reporter spec或--grep 2.2.4。
以编程方式 这个包的主要导出是一个函数,允许你对适配器运行测试:
var promisesAplusTests = require("promises-aplus-tests"); promisesAplusTests(adapter, function (err) { // All done; output is in the console. Or check `err` for number of failures. });
你也可以将任何Mocha选项作为第二个参数传递,例如:
promisesAplusTests(adapter, { reporter: "dot" }, function (err) { // As before. });
; 在现有的Mocha测试套件中
如果你已经有一个Mocha测试套件,并希望将这些测试包含在其中,你可以这样做:
describe("Promises/A+ Tests", function () { require("promises-aplus-tests").mocha(adapter); });
如果你的Mocha测试在浏览器中运行,这也适用,只要你使用browserify即可。
* 测试手写promise是否符合promise/a+规范
* 核心步骤
* 1.使用CommonJS模块化语法暴露出去
* 1.1提供deferred方法 返回对象[promise,reslove,reject]
* 1.2 promise padding状态的promise实例
* 1.3 reslove 以传入的原因兑现promise
* 1.4 reject 以传入的原因拒绝promise
* 2. 下包
* 2.1初始化项目 npm init -y
* 2.2npm install promises-aplus-tests -D
* 3.配置script promises-aplus-tests 代码文件
*
module.exports = {
deferred() {
const res = {};
res.promise = new myPromise((resolve, reject) => {
res.resolve = resolve;
res.reject = reject;
});
return res;
},
};
代码
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class myPromise {
// 状态
state = PENDING;
// 原因
result = undefined;
// 回调函数数组
#handlers = []; // [{onFulfilled,onRejected}...]
// 构造函数
constructor(func) {
// pending->fulfilled
const resolve = (result) => {
if (this.state === PENDING) {
this.state = FULFILLED;
this.result = result;
this.#handlers.forEach(({ onFulfilled }) => {
onFulfilled(this.result);
});
}
};
// pending->rejected
const reject = (result) => {
if (this.state === PENDING) {
this.state = REJECTED;
this.result = result;
this.#handlers.forEach(({ onRejected }) => {
onRejected(this.result);
});
}
};
try {
func(resolve, reject);
} catch (error) {
reject(error);
}
}
// then方法
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (x) => x;
onRejected =
typeof onRejected === "function"
? onRejected
: (x) => {
throw x;
};
const p2 = new myPromise((resolve, reject) => {
if (this.state === FULFILLED) {
runAsynctask(() => {
try {
const x = onFulfilled(this.result);
resolvePromise(p2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.state === REJECTED) {
runAsynctask(() => {
try {
const x = onRejected(this.result);
resolvePromise(p2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
} else if (this.state === PENDING) {
this.#handlers.push({
onFulfilled: () => {
runAsynctask(() => {
try {
const x = onFulfilled(this.result);
resolvePromise(p2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
},
onRejected: () => {
runAsynctask(() => {
try {
const x = onRejected(this.result);
resolvePromise(p2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
},
});
}
});
return p2;
}
// catch方法
catch(onRejected) {
return this.then(undefined, onRejected);
}
// finally方法
finally(onFinally) {
return this.then(onFinally, onFinally);
}
// 静态方法-resolve
static resolve(value) {
if (value instanceof myPromise) {
return value;
}
return new myPromise((resolve) => {
resolve(value);
});
}
// 静态方法-reject
static reject(value) {
return new myPromise((undefined, reject) => {
reject(value);
});
}
// 静态方法-race
static race(promises) {
return new myPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError("Argument is not iterable"));
}
promises.forEach((p) => {
myPromise.resolve(p).then(
(res) => {
resolve(res);
},
(err) => {
reject(err);
}
);
});
});
}
// 静态方法-all
static all(promises) {
return new myPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError("Argument is not iterable"));
}
promises.length === 0 && resolve(promises);
const results = [];
let count = 0;
promises.forEach((p, index) => {
myPromise.resolve(p).then(
(res) => {
results[index] = res;
count++;
count === promises.length && resolve(results);
},
(err) => {
reject(err);
}
);
});
});
}
// 静态方法-allSettled
static allSettled(promises) {
return new myPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError("Argument is not iterable"));
}
promises.length === 0 && resolve(promises);
const results = [];
let count = 0;
promises.forEach((p, index) => {
myPromise.resolve(p).then(
(res) => {
results[index] = { status: FULFILLED, value: res };
count++;
count === promises.length && resolve(results);
},
(err) => {
results[index] = { status: REJECTED, reason: err };
count++;
count === promises.length && resolve(results);
}
);
});
});
}
// 静态方法-any
static any(promises) {
return new myPromise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError("Argument is not iterable"));
}
promises.length === 0 &&
reject(new AggregateError(promises, "All promises were rejected"));
const errors = [];
let count = 0;
promises.forEach((p, index) => {
myPromise.resolve(p).then(
(res) => {
resolve(res);
},
(err) => {
errors[index] = err;
count++;
count === promises.length &&
reject(new AggregateError(errors, "All promises were rejected"));
}
);
});
});
}
}
function runAsynctask(callback) {
if (typeof queueMicrotask === "function") {
queueMicrotask(callback);
} else if (typeof MutationObserver === "function") {
const obs = new MutationObserver(callback);
const divNode = document.createElement("div");
obs.observe(divNode, { childList: true });
divNode.innerText = "Javascript";
} else {
setTimeout(callback, 0);
}
}
function resolvePromise(p2, x, resolve, reject) {
// 如果p2和x引用同一个对象,通过TypeError作为原因来拒绝pormise
if (x === p2) {
throw new TypeError("Chaining cycle detected for promise");
}
/**
* 如果x是一个promise,采用他的状态
* 如果x是pengding状态,promise必须保持等待状态,直到x被fulfilled或rejected
* 如果x是fulfilled状态,用相同的原因解决promise
* 如果x是rejected状态,用相同的原因拒绝promise
* */
if (x instanceof myPromise) {
x.then((y) => {
resolvePromise(p2, y, resolve, reject);
}, reject);
}
// 如果x是一个对象或者函数
else if (x !== null && (typeof x === "object" || typeof x === "function")) {
// 让then成为x.then
try {
var then = x.then;
} catch (e) {
// 如果检索属性x.then抛出了异常e,用e作为原因拒绝promise
return reject(e);
}
/**
* 如果then是一个函数,通过call调用他,并且将x作为他的this(参数1)
* 调用then时传入2个回调函数:
* 第一个参数叫做resolvePromise(对应到的参数2)
* 第二个参数叫做rejectPromise(对应到参数3)
* */
if (typeof then === "function") {
// 如果 resolvePromise 和 rejectPromise 均被调用,或者同一参数被调用了多次,只采用第一次调用,后续的调用会被忽略(观察called后续的赋值+判断)
let called = false;
try {
then.call(
x,
// 如果 resolvePromise 以 成功原因 y 为参数被调用,继续执行 resolvePromise
(y) => {
if (called) return;
called = true;
resolvePromise(p2, y, resolve, reject);
},
// 如果 rejectPromise 以拒绝原因 r 为参数被调用,用 r 拒绝 promise
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} catch (e) {
// 如果调用then抛出异常
// 如果resolvePromise或rejectPromise已经被调用,忽略它
if (called) return;
called = true;
// 否则以 e 作为拒绝原因 拒绝promise
reject(e);
}
} else {
// 如果then不是函数,用 x 作为原因 兑现promise
resolve(x);
}
} else {
// 如果then不是对象或函数,用 x 作为原因 兑现promise
return resolve(x);
}
}
module.exports = {
deferred() {
const res = {};
res.promise = new myPromise((resolve, reject) => {
res.resolve = resolve;
res.reject = reject;
});
return res;
},
};