【发布时间】:2016-08-12 03:12:27
【问题描述】:
在测试中,我发现 JavaScript Promises总是是异步的,无论它们的链中是否包含任何异步函数。
这是一些显示控制台中操作顺序的代码。如果您运行它,您会看到即使每个函数都是同步的,输出也会显示两个 aPromise() 调用并行运行,而 "surprisingly this happens after run 2 finishes" 不会在运行 2 完成之前发生。
function aPromise() {
return new Promise(function(resolve, reject) {
console.log("making promise A")
resolve(bPromise());
console.log("promise A resolved")
});
}
function bPromise() {
return new Promise(function(resolve, reject) {
console.log("making and resolving promise B")
resolve();
});
}
aPromise().then(function() {
console.log("finish run 1");
}).then(function() {
console.log("surprisingly this happens after run 2 finishes");
});
aPromise().then(function() {
console.log("finish run 2");
})
输出到控制台:
making promise A
making and resolving promise B
promise A resolved
making promise A
making and resolving promise B
promise A resolved
finish run 1
finish run 2
surprisingly this happens after run 2 finishes
那么,为什么 JavaScript 的 promises 在只调用同步函数时是异步的?导致这种行为的幕后发生了什么?
附:为了更好地理解这一点,我实现了我自己的 Promise 系统,我发现让同步函数按照预期的顺序发生很容易,但是让它们并行发生是我只能通过在每个时间设置几毫秒的 setTimeout() 来完成的。解决(我的猜测是,这不是香草承诺发生的事情,它们实际上是多线程的)。
这对我的一个程序来说是一个小问题,我正在遍历树,将函数数组应用于每个节点,如果该节点已经运行了异步函数,则将函数放入队列中。大多数函数都是同步的,因此很少使用队列,但是在从回调(地狱)切换到 Promises 时,我遇到了一个问题,即由于 Promises 从不同步运行,队列几乎总是被使用。这不是一个大问题,但它有点像调试噩梦。
1 年后编辑
我最终编写了一些代码来处理这个问题。它不是非常彻底,但我已经成功地使用它来解决我遇到的问题。
var SyncPromise = function(fn) {
var syncable = this;
syncable.state = "pending";
syncable.value;
var wrappedFn = function(resolve, reject) {
var fakeResolve = function(val) {
syncable.value = val;
syncable.state = "fulfilled";
resolve(val);
}
fn(fakeResolve, reject);
}
var out = new Promise(wrappedFn);
out.syncable = syncable;
return out;
}
SyncPromise.resolved = function(result) {
return new SyncPromise(function(resolve) { resolve(result); });
}
SyncPromise.all = function(promises) {
for(var i = 0; i < promises.length; i++) {
if(promises[i].syncable && promises[i].syncable.state == "fulfilled") {
promises.splice(i, 1);
i--;
}
// else console.log("syncable not fulfilled" + promises[i].syncable.state)
}
if(promises.length == 0)
return SyncPromise.resolved();
else
return new SyncPromise(function(resolve) { Promise.all(promises).then(resolve); });
}
Promise.prototype.syncThen = function (nextFn) {
if(this.syncable && this.syncable.state == "fulfilled") {
//
if(nextFn instanceof Promise) {
return nextFn;
}
else if(typeof nextFn == "function") {
var val = this.syncable.value;
var out = nextFn(val);
return new SyncPromise(function(resolve) { resolve(out); });
}
else {
PINE.err("nextFn is not a function or promise", nextFn);
}
}
else {
// console.log("default promise");
return this.then(nextFn);
}
}
【问题讨论】:
-
其他人可能会挖掘出具体的讨论,但总体理念是一致性/可预测性是界面设计的一个优点。一些早期的 Promise 实现并非如此,但随着人们被意外的错误所困扰,这种情绪发生了变化。如果你知道 Promise 总是被异步处理,那么推理 Promise 会容易很多。
-
你知道什么样的bug吗?
-
快速查看the spec,措辞是“将立即enqueue要调用的作业”,enqueued 事物可以调用的最快时间在当前函数结束后立即出现。
new Promise(r => r()).then(() => console.log('yay')); let i = 10; while (--i) console.log(i);在 yay 之前记录 9..1 -
@SephReed:
getPromise().then(function(x) { foo.bar(x); }); var foo = new Foo();的那种错误。它适用于 A+ 承诺,但不适用于then意外同步调用其回调。
标签: javascript asynchronous promise synchronous