0

I don't understand the order in which tasks are sent to the microtasks queue.
I expected this output: 1, 11, 111, 2, 3, 12, 122
But received: 1, 11, 111, 2, 12, 122, 3
Does compiler register the whole chain or it can see only the next then?
How does it work?

 Promise.resolve() .then(() => console.log(1)) .then(() => console.log(2)) .then(() => console.log(3)); Promise.resolve() .then(() => console.log(11)) .then(() => console.log(12)); Promise.resolve() .then(() => console.log(111)) .then(() => console.log(122));

4

2 Answers 2

-2

The order of the console logs are not guaranteed. There are 45 possible outcomes each time you run your script.

Here are your rules:

  • Main
    • 1 before 11
    • 11 before 111
  • Secondary
    • 2 follows 1
    • 3 follows 2
    • 12 follows 11
    • 122 follows 111

Demonstration

I put together a naïve script that will generate each permutation and validate the order based on the promise logic you have provided.

const sequence = [1, 2, 3, 11, 12, 111, 122]; const rules = [ // All Promises { type: 'condition', operator: 'lt', left: 1, right: 11 }, { type: 'condition', operator: 'lt', left: 11, right: 111 }, // 1st Promise { type: 'condition', operator: 'gt', left: 2, right: 1 }, { type: 'condition', operator: 'gt', left: 3, right: 2 }, // 2nd Promise { type: 'condition', operator: 'gt', left: 12, right: 11 }, // 3rd Promise { type: 'condition', operator: 'gt', left: 122, right: 111 } ]; const main = () => { const validPermutations = permutator(sequence) .filter(p => validate(p, rules)); validPermutations.forEach((p, i) => { console.log(`Permutation #${i + 1}: ${JSON.stringify(p).replace(/,/g, ', ')}`) }); console.log('Valid permutations:', validPermutations.length); }; const validate = (sequence, rules) => { return rules.every(rule => { switch (rule.type) { case 'condition': const indexLeft = sequence.indexOf(rule.left), indexRight = sequence.indexOf(rule.right); switch (rule.operator) { case 'gt': return indexLeft > indexRight; case 'lt': return indexLeft < indexRight; default: return false; } break; default: return false; } }); }; const permutator = (inputArr) => { const result = []; permute(inputArr, result); return result; } const permute = (arr, res, m = []) => { if (arr.length === 0) res.push(m); else { for (let i = 0; i < arr.length; i++) { const curr = arr.slice(), next = curr.splice(i, 1); permute(curr.slice(), res, m.concat(next)); } } } main();
.as-console-wrapper { top: 0; max-height: 100% !important; }

Sign up to request clarification or add additional context in comments.

Comments

-2

Linked Promise.then calls are nested, not sequential

According to MDN's Microtask guide,

if a microtask adds more microtasks to the queue by calling queueMicrotask(), those newly-added microtasks execute before the next task is run.

However, this does not apply to linked .then calls. If it did, you would have to operate under the assumption that linked .then calls are all linked to the original Promise and not each other. You can actually see that each linked .then call is linked to the result of the previous .then:

Promise.resolve(3) .then(x => { // Receives 3 from original Promise console.log(x) x = x ** 3 return x }) .then(x => { // Receives 27 from previous .then console.log(x) x = x ** 3 return x }) .then(x => { // Receives 19683 from previous .then console.log(x) return x })

In this example, you can see that in order for the next .then to be added to the queue, it needs the result of the previous .then call.

To further illustrate that linked Promise.then calls are nested and not sequential, you can create both nested and sequential microtasks to see what happens.

Microtasks

Nested

When this example is run, it clearly illustrates the same results you received from linking .then calls.

queueMicrotask(() => { console.log('A1') queueMicrotask(() => { console.log('A2') queueMicrotask(() => { console.log('A3') }) }) }) queueMicrotask(() => { console.log('B1') queueMicrotask(() => { console.log('B2') queueMicrotask(() => { console.log('B3') }) }) })

Sequential

When you run this example, it demonstrates the aforementioned quote from MDN. It functions similarly to how you thought linking .then calls would, but this is not how Promises work.

queueMicrotask(() => { console.log('A1') queueMicrotask(() => console.log('A2')) queueMicrotask(() => console.log('A3')) queueMicrotask(() => console.log('A4')) }) queueMicrotask(() => { console.log('B1') queueMicrotask(() => console.log('B2')) queueMicrotask(() => console.log('B3')) queueMicrotask(() => console.log('B4')) })

Promise conceptualization

If you don't care about this part, you can skip it. This is simply a quick implementation of a Promise using queueMicrotask. Hopefully it can help you see the nesting of linked .then calls and their associated microtasks.

class MicroPromise { #result = undefined #thens = [] constructor(callback) { queueMicrotask(() => { callback(result => { this.#result = result this.#callThens() }) }) } then(callback) { return new MicroPromise(resolve => { this.#thens.push({resolve, callback}) }) } #callThens() { queueMicrotask(() => { // Allows us to wait for `.then` MicroPromise constructor microtasks to execute for (const then of this.#thens) { queueMicrotask(() => then.resolve(then.callback(this.#result))) } }) } static resolve(result) { return new MicroPromise(resolve => resolve(result)) } } MicroPromise.resolve() .then(() => console.log('A1')) .then(() => console.log('A2')) .then(() => console.log('A3')) MicroPromise.resolve() .then(() => console.log('B1')) .then(() => console.log('B2')) .then(() => console.log('B3'))

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.