什么是 promise ?
Promise是一个thenable
,其行为符合Promises/A+规范。thenable
是具有sau đó
方法的任何对象或函数。
这是一个 promise 的样子:
var promise = {
...
then: function (onFulfilled, onRejected) { ... },
...
};
这是我们从一开始就了解的唯一 promise (不包括其行为)。
了解Promises / A +规范
Promises / A +规范分为三个主要部分:
promise 状态
sau đó
方法
promise 解决程序
该规范没有提及如何创建,履行或拒绝 promise 。
因此,我们将从创建这些函数开始:
function deferred() { ... } // returns an object { promise, resolve, reject }
function fulfill(promise, value) { ... } // fulfills promise with value
function reject(promise, reason) { ... } // rejects promise with reason
尽管没有创建 promise 的标准方法,但是
tests仍然要求我们公开
deferred
函数。因此,我们将仅使用
deferred
创建新的Promise:
deferred()
:创建一个由{ promise, resolve, reject }
组成的对象:
promise
是当前处于待处理状态的Promise。
resolve(value)
sử dụnggiá trị
解决了 promise 。
reject(reason)
将 promise 从挂起状态转移到拒绝状态,拒绝原因为reason
。
Đây là
deferred
函数的部分实现:
function deferred() {
var call = true;
var promise = {
then: undefined,
...
};
trở lại {
promise: promise,
resolve: function (value) {
if (call) {
call = false;
resolve(promise, value);
}
},
reject: function (reason) {
if (call) {
call = false;
reject(promise, reason);
}
}
};
}
N.B.
promise
对象仅具有sau đó
属性,当前为không xác định
。我们仍然需要决定sau đó
函数应该是什么以及promise对象应该具有哪些其他属性(即promise对象的形状)。该决定还将影响fulfill
Vàreject
函数的实现。
resolve(promise, value)
Vàreject(promise, value)
函数只能被调用一次,如果我们调用一个,那么我们就不能调用另一个。因此,我们将它们包装在一个闭包中,并确保它们之间仅被调用一次。
我们在deferred
的定义中引入了一个新函数,即 promise 解决程序resolve(promise, value)
。规范将该函数表示为[[Resolve]](promise, x)
。该功能的实现完全由规范规定。因此,我们接下来将实现它。
function resolve(promise, x) {
// 2.3.1. If promise and x refer to the same object,
// reject promise with a TypeError as the reason.
if (x === promise) return reject(promise, new TypeError("Self resolve"));
// 2.3.4. If x is not an object or function, fulfill promise with x.
var type = typeof x;
if (type !== "object" && type !== "function" || x === null)
return fulfill(promise, x);
// 2.3.3.1. Let then be x.then.
// 2.3.3.2. If retrieving the property x.then results in a thrown exception e,
// reject promise with e as the reason.
thử {
var then = x.then;
} catch (e) {
return reject(promise, e);
}
// 2.3.3.4. If then is not a function, fulfill promise with x.
if (typeof then !== "function") return fulfill(promise, x);
// 2.3.3.3. If then is a function, call it with x as this, first argument
// resolvePromise, and second argument rejectPromise, where:
// 2.3.3.3.1. If/when resolvePromise is called with a value y,
// run [[Resolve]](promise, y).
// 2.3.3.3.2. If/when rejectPromise is called with a reason r,
// reject promise with r.
// 2.3.3.3.3. If both resolvePromise and rejectPromise are called,
// or multiple calls to the same argument are made,
// the first call takes precedence, and any further calls are ignored.
// 2.3.3.3.4. If calling then throws an exception e,
// 2.3.3.3.4.1. If resolvePromise or rejectPromise have been called, ignore it.
// 2.3.3.3.4.2. Otherwise, reject promise with e as the reason.
promise = deferred(promise);
thử {
then.call(x, promise.resolve, promise.reject);
} catch (e) {
promise.reject(e);
}
}
N.B.
我们省略了section 2.3.2,因为它是一种优化的方法,它取决于promise对象的形状。我们将在本节结束时再次访问。
如上所述,section 2.3.3.3的描述比实际代码长得多。这是因为巧妙地破解了promise = deferred(promise)
,使我们能够重用deferred
函数的逻辑。这样可以确保promise.resolve
Vàpromise.reject
在两者之间只能调用一次。我们只需要对deferred
函数做些小改动就可以使该hack工作。
function deferred(promise) {
var call = true;
promise = promise || {
then: undefined,
...
};
return /* the same object as before */
}
promise 状态和
sau đó
方法
我们已经将决定 promise 对象的形状的问题推迟了很长时间,但是我们不能再延迟了,因为
fulfill
Và
reject
函数的实现都依赖于它。现在该阅读规范对 promise 状态要说的内容了:
A promise must be in one of three states: pending, fulfilled, or rejected.
- When pending, a promise:
- may transition to either the fulfilled or rejected state.
- When fulfilled, a promise:
- must not transition to any other state.
- must have a value, which must not change.
- When rejected, a promise:
- must not transition to any other state.
- must have a reason, which must not change.
Here, “must not change” means immutable identity (i.e. ===
), but does not imply deep immutability.
我们如何知道 promise 目前处于哪个状态?我们可以做这样的事情:
var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
var promise = {
then: function (onFulfilled, onRejected) { ... },
state: PENDING | FULFILLED | REJECTED, // vertical bar is not bitwise or
...
};
但是,还有更好的选择。由于 promise 状态只能通过其
sau đó
方法观察到(即,根据 promise 状态,
sau đó
方法的行为有所不同),我们可以创建与这三种状态相对应的三个专门的
sau đó
函数:
var promise = {
then: pending | fulfilled | rejected,
...
};
function pending(onFulfilled, onRejected) { ... }
function fulfilled(onFulfilled, onRejected) { ... }
function rejected(onFulfilled, onRejected) { ... }
另外,我们还需要一个属性来保存 promise 的数据。当 promise 待定时,数据是
onFulfilled
Và
onRejected
回调的队列。当 promise 兑现时,数据就是 promise 的值(value)。当 promise 被拒绝时,数据就是 promise 的原因。
当我们创建一个新的Promise时,初始状态为待定,初始数据为空队列。因此,我们可以完成
deferred
函数的实现,如下所示:
function deferred(promise) {
var call = true;
promise = promise || {
then: pending,
data: []
};
return /* the same object as before */
}
另外,既然我们知道了promise对象的形状,那么我们终于可以实现
fulfill
Và
reject
函数:
function fulfill(promise, value) {
setTimeout(send, 0, promise.data, "onFulfilled", value);
promise.then = fulfilled;
promise.data = value;
}
function reject(promise, reason) {
setTimeout(send, 0, promise.data, "onRejected", reason);
promise.then = rejected;
promise.data = reason;
}
function send(queue, callback, data) {
for (var item of queue) item[callback](data);
}
我们需要使用
setTimeout
,因为根据规范
section 2.2.4,在执行上下文堆栈仅包含平台代码之前,不得调用
onFulfilled
hoặc
onRejected
。
接下来,我们需要实现
pending
,
fulfilled
Và
rejected
函数。我们将从
pending
函数开始,该函数将
onFulfilled
Và
onRejected
回调推入队列并返回新的promise:
function pending(onFulfilled, onRejected) {
var future = deferred();
this.data.push({
onFulfilled: typeof onFulfilled === "function" ?
compose(future, onFulfilled) : future.resolve,
onRejected: typeof onRejected === "function" ?
compose(future, onRejected) : future.reject
});
return future.promise;
}
function compose(future, fun) {
return function (data) {
thử {
future.resolve(fun(data));
} catch (reason) {
future.reject(reason);
}
};
}
我们需要测试
onFulfilled
Và
onRejected
是否为函数,因为根据规范的
section 2.2.1,它们是可选参数。如果提供了
onFulfilled
Và
onRejected
,则根据规范的
section 2.2.7.1Và
section 2.2.7.2将它们与递延值组成。否则,它们会按照规范的
section 2.2.7.3Và
section 2.2.7.4短路。
最后,我们实现
fulfilled
Và
rejected
函数,如下所示:
function fulfilled(onFulfilled, onRejected) {
return bind(this, onFulfilled);
}
function rejected(onFulfilled, onRejected) {
return bind(this, onRejected);
}
function bind(promise, fun) {
if (typeof fun !== "function") return promise;
var future = deferred();
setTimeout(compose(future, fun), 0, promise.data);
return future.promise;
}
有趣的是,可以在上面恰当命名的
bind
函数中看到
promises are monads。至此,我们对Promises / A +规范的实现已完成。
优化
resolve
规范的
Section 2.3.2描述了当
resolve(promise, x)
被确定为一个 promise 时对
x
函数的优化。这是优化的
resolve
函数:
function resolve(promise, x) {
if (x === promise) return reject(promise, new TypeError("Self resolve"));
var type = typeof x;
if (type !== "object" && type !== "function" || x === null)
return fulfill(promise, x);
thử {
var then = x.then;
} catch (e) {
return reject(promise, e);
}
if (typeof then !== "function") return fulfill(promise, x);
// 2.3.2.1. If x is pending, promise must remain pending until x is
// fulfilled or rejected.
if (then === pending) return void x.data.push({
onFulfilled: function (value) {
fulfill(promise, value);
},
onRejected: function (reason) {
reject(promise, reason);
}
});
// 2.3.2.2. If/when x is fulfilled, fulfill promise with the same value.
if (then === fulfilled) return fulfill(promise, x.data);
// 2.3.2.3. If/when x is rejected, reject promise with the same reason.
if (then === rejected) return reject(promise, x.data);
promise = deferred(promise);
thử {
then.call(x, promise.resolve, promise.reject);
} catch (e) {
promise.reject(e);
}
}
全部放在一起
该代码以
gist的形式提供。您可以简单地下载它并运行测试套件:
$ npm install promises-aplus-tests -g
$ promises-aplus-tests promise.js
不用说,所有测试都通过了。
Tôi là một lập trình viên xuất sắc, rất giỏi!