我可以依赖未链接的后续诺言订单吗?

时间:2018-08-29 00:32:19

标签: javascript promise

众所周知,可以将多个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()实例很困难。

2 个答案:

答案 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或工作程序之间的诺言不同),则此保证不成立。

话虽如此-强烈建议不要像这样依赖执行顺序。