0

I was under the impression that then handler functions are processed in the order they are added.

For example, if you run this code:

function getResolvedPromise(result) {
  return new Promise((resolve, reject) => {
    console.log(`Promise executor, result will be ${result}`);
    resolve(result);
  });
}

function logThen(label, result) {
  console.log(`[${label}] Promise then, result = ${result}`);
  return result;
}

// Shows that then handlers get run in the order they are added.

const prom1 = getResolvedPromise("prom1");
const prom2 = getResolvedPromise("prom2");

prom2.then(logThen.bind(null, "1"));
prom2.then(logThen.bind(null, "2"));
prom2.then(logThen.bind(null, "3"));

prom1.then(logThen.bind(null, "4"));
prom1.then(logThen.bind(null, "5"));
prom1.then(logThen.bind(null, "6"));

Then this is what gets logged. This makes sense to me.

Promise executor, result will be prom1
Promise executor, result will be prom2
[1] Promise then, result = prom2
[2] Promise then, result = prom2
[3] Promise then, result = prom2
[4] Promise then, result = prom1
[5] Promise then, result = prom1
[6] Promise then, result = prom1

However, if you run this:

// Order of execution is interleaved when then handlers are directly attached?

const prom1 = getResolvedPromise("prom1")
  .then(logThen.bind(null, "1"))
  .then(logThen.bind(null, "2"))
  .then(logThen.bind(null, "3"));

const prom2 = getResolvedPromise("prom2")
  .then(logThen.bind(null, "4"))
  .then(logThen.bind(null, "5"))
  .then(logThen.bind(null, "6"));

Then this is what gets logged.

Promise executor, result will be prom1
Promise executor, result will be prom2
[1] Promise then, result = prom1
[4] Promise then, result = prom2
[2] Promise then, result = prom1
[5] Promise then, result = prom2
[3] Promise then, result = prom1
[6] Promise then, result = prom2

What explains this interleaving behavior?

Note that the specification contains some relevant information here. Specifically, it says this about HostEnqueuePromiseJob:

Jobs must run in the same order as the HostEnqueuePromiseJob invocations that scheduled them.

And if we look at the specification for Promise.prototype.then, we see that it calls HostEnqueuePromiseJob. So the only way this makes sense to me is that the then calls are being called in an interleaved order. But... I don't really get how this happens, since the two code samples I posted seem equivalent to me.

arcticmatt
  • 1,956
  • 1
  • 19
  • 36
  • https://stackoverflow.com/questions/54441138/execution-order-of-promises-in-node-js is a similar question, but I don't find the answers satisfactory. – arcticmatt Sep 12 '20 at 17:08
  • 1
    Yes, they run in the order they are attached, but that holds only for handles attached to the *same* promise. Your chained `.then()` calls return a new promise each time, and they fulfill at different points in time. – Bergi Sep 12 '20 at 17:11
  • How is chaining `.then()` calls directly on a promise different than doing it on separate lines, like I did in the first example? I.e. I don't get why the two examples I posted have different behavior. – arcticmatt Sep 12 '20 at 17:15
  • It's the difference between `const prom1 = Promise.resolve(); prom1.then(…); prom1.then(…);` and `const prom1 = Promise.resolve(); const prom2 = prom1.then(…); prom2.then(…);` Notice that `prom1 !== prom2` in there. – Bergi Sep 12 '20 at 17:20
  • Sorry, I don't get how that example clarifies my question. Here's your example: https://hastebin.com/eqotilucag.js. This already makes sense to me. – arcticmatt Sep 12 '20 at 17:24
  • In there, you're still attaching the handlers 1 and 2 to the same `prom1`. Try to rewrite with 4 promises, i.e. `const prom1 = resolved(), prom2 = prom1.then(…), prom3 = resolved(), prom4 = prom3.then(…);` – Bergi Sep 12 '20 at 17:29
  • It would help if you wrote out a working example :) As it is, I'm not sure what you're trying to say. And wrt to "Yes, they run in the order they are attached, but that holds only for handles attached to the same promise.", that doesn't appear to be true. For example, see the first code snippet in my post. Also, based on the spec, it sounds like the the ordering should be global, i.e. if two calls to HostEnqueuePromiseJob are made for different promises, the jobs will run in the order they were enqueued. – arcticmatt Sep 12 '20 at 17:34
  • I don't see how your first snippet contradicts my claim. The callbacks run exactly in the same order in which their `.then()` callbacks happened, which immediately scheduled them. In your second snippet, even the global ordering still holds: the callbacks are executed in the order they were queued. However, they are not getting queued before the promise they are registered on does fulfill – Bergi Sep 12 '20 at 17:41
  • Ah, I see what you're saying. I'll write up a more detailed answer to this question based on the spec if it gets re-opened. – arcticmatt Sep 12 '20 at 18:05

0 Answers0