There's something about chaining promises that I don't understand. The Node.js snippet below produces the following output. Why is promise.allSettled
called after the first sleep
on line 18 and not after the 2nd one on line 21?
Cycle 0 is going to sleep promise.js:2
Cycle 0 slept for 2 seconds promise.js:6
Returned from sleep function promise.js:19
Cycle 0 is going to sleep promise.js:2
Done with the process promise.js:27
Cycle 0 slept for 2 seconds promise.js:6
function sleep(cycle) {
console.log(`Cycle ${ cycle } is going to sleep`);
return new Promise(resolve => {
setTimeout(() => {
console.log(`Cycle ${ cycle } slept for 2 seconds`);
resolve();
}, 2000);
});
}
function process() {
let cycles = 1;
let subprocesses = [];
for (let i = 0; i < cycles; i++) {
subprocesses.push(
sleep(i).then(() => {
console.log('Returned from sleep function');
sleep(i);
})
);
}
Promise.allSettled(subprocesses).then(results => {
console.log('Done with the process');
});
}
process();
Because you haven't resolved the promise created sleep(i).then
to the promise from the second sleep
, so it doesn't wait for that second operation to finish before settling. You need a return
in front of the second call:
function sleep(cycle) {
console.log(`Cycle ${ cycle } is going to sleep`);
return new Promise(resolve => {
setTimeout(() => {
console.log(`Cycle ${ cycle } slept for 2 seconds`);
resolve();
}, 2000);
});
}
function process() {
let cycles = 1;
let subprocesses = [];
for (let i = 0; i < cycles; i++) {
subprocesses.push(
sleep(i).then(() => {
console.log('Returned from sleep function');
return sleep(i); // <============================
})
);
}
Promise.allSettled(subprocesses).then(results => {
console.log('Done with the process');
});
}
process();
Here's a version with two cycles, and with the sleep calls more clearly marked:
function sleep(cycle, sub) {
console.log(`Cycle ${cycle}(${sub}) is going to sleep`);
return new Promise(resolve => {
setTimeout(() => {
console.log(`Cycle ${cycle}(${sub}) slept for 2 seconds`);
resolve();
}, 2000);
});
}
function process() {
let cycles = 2;
let subprocesses = [];
for (let i = 0; i < cycles; i++) {
subprocesses.push(
sleep(i, "a").then(() => {
console.log(`Returned from sleep function (${i})`);
return sleep(i, "b"); // <============================
})
);
}
Promise.allSettled(subprocesses).then(results => {
console.log('Done with the process');
});
}
process();
It might be clearer to split that sleep
, sleep
serial operation off into its own function, perhaps an async
function:
async function twoSleep(i) {
await sleep(i, "a");
console.log(`Returned from sleep function (${i})`);
await sleep(i, "b");
}
function process() {
let cycles = 2;
let subprocesses = [];
for (let i = 0; i < cycles; i++) {
subprocesses.push(twoSleep(i));
}
Promise.allSettled(subprocesses).then(results => {
console.log('Done with the process');
});
}
function sleep(cycle, sub) {
console.log(`Cycle ${cycle}(${sub}) is going to sleep`);
return new Promise(resolve => {
setTimeout(() => {
console.log(`Cycle ${cycle}(${sub}) slept for 2 seconds`);
resolve();
}, 2000);
});
}
async function twoSleep(i) {
await sleep(i, "a");
console.log(`Returned from sleep function (${i})`);
await sleep(i, "b");
}
function process() {
let cycles = 2;
let subprocesses = [];
for (let i = 0; i < cycles; i++) {
subprocesses.push(twoSleep(i));
}
Promise.allSettled(subprocesses).then(results => {
console.log('Done with the process');
});
}
process();