众所周知,可以将多个Promises组合成一个链,从而准确地onFulfilled
回调一个接一个地调用(尽管它们彼此之间是异步的):
Promise.resolve()
.then(() => {
console.log("i will be the first one");
})
.then(() => {
console.log("i will be the second one"); // definetely second
})
;
但是非链式后续承诺呢? 最简单的例子:
Promise.resolve()
.then(() => console.log("i will be the first one"))
;
Promise.resolve()
.then(() => console.log("i will be the second one")) // sure?
;
我天真地认为,Promises回调通过事件队列工作(类似于setTimeout
中的计时器事件),这样,首先Promise.resolve()
在第二个事件执行此操作之前将其事件推入队列,因此第一个回调在第二个之前被调用。
但是我不确定对此是否有任何保证。我可以依靠它还是异步乐透?有人知道规格说明了什么?
更新
一些人注意到最简单的例子是没有用的,所以我想解释一下我原来的问题。
我有一个可以懒惰地初始化另一个类的实例并为托管实例提供get方法的类:
class Lazy {
/** @param {Class} T */
constructor(T) { }
/** @returns {Promise.<T>} */
instance() {
// there will be complex async initialization at first call
// and Promise.resolve() at following calls
}
}
class Foo {
on() { }
off() { }
}
/** @type {Lazy.<Foo>} */
let foo = new Lazy(Foo);
foo.instance().then((i) => i.on());
foo.instance().then((i) => i.off());
最后两行显示了我的问题-当我不确定Foo
会在on()
之前被调用时,以这种方式使用off()
实例很困难。
答案 0 :(得分:1)
承诺回调通过事件队列起作用
这不是事实。回调以先返回先服务为基础。启动操作时的顺序并不重要,这取决于他们返回的顺序,从而决定将首先处理哪个操作。
在您的示例中,如果“第一个”异步操作返回的时间比“第二个”异步操作花费的时间长,那么“第二个” then
将首先被处理。
显然,这些操作返回所需的时间有很多变量。网络速度,服务器上的负载(或您使用的任何异步服务),promise引擎的浏览器实现等。因此,您必须假设您不知道何时处理这些信息
要避免出现竞争状况,如果您需要按照特定的顺序排列某些东西,请使用callbacks / .then
/ await
/ etc来确保它们按该顺序运行。您不能依靠调用该操作的顺序。
答案 1 :(得分:1)
最后两行显示了我的问题-当我不确定在off()之前会调用on()时,很难以这种方式使用Foo实例。
您不应依赖此行为,而应await
获取实例,然后对其进行缓存:
async function withInstance() {
let instance = await foo.instance();
await instance.on();
await instance.off(); // guaranteed to be called after `.on`.
}
您可以依靠Job
s的执行顺序。
添加承诺then
时,它就是规范中的EnqueueJob
。引用Jobs and Job Queues:
单个作业队列中的PendingJob记录始终以FIFO顺序启动。
请注意,如果您有多个上下文(例如,iframe或工作程序之间的诺言不同),则此保证不成立。
话虽如此-强烈建议不要像这样依赖执行顺序。