In a comment I said:
"It is important that each task complete successfully before continuing to the next" That isn't what the code above it does, unless doTask2 and doTask3 don't do their tasks, but return functions that will do them when called later.
You replied:
"That isn't what the code above it does" - I think it does - each of those methods return a promise which is fulfilled when the task completes.
That may be the basis of your problem. then
and catch
expect functions, not promises. The code I was referring to is:
server.doTask1()
.then(server.doTask2())
.then(server.doTask3())
.catch( ... handle the error )
When that code runs, the following happens synchronously:
server.doTask1
is called, starting its work and returning a promise (let's call it "p1")
server.doTask2
is called, starting its work and returning a promise ("p2")
then
is called on "p1", passing in "p2"; it returns a promise ("p3")
server.doTask3
is called, starting its work and returning a promise ("p4")
then
is called on "p3", passing in "p4"; it returns a promise ("p5")
catch
is called on "p5" (returning a promise that's never used)
At that point, the tasks started by doTask
, doTask2
, and doTask3
are all running in parallel; they'll settle their promises, but there's no order imposed between the tasks. You can see that here:
const start = Date.now();
const elapsed = () => String(Date.now() - start).padStart(4);
const log = msg => console.log(`[${elapsed()}] ${msg}`);
const server = {
doTask1() {
return new Promise(resolve => {
log("task1 started");
setTimeout(() => {
log("task1 complete");
resolve("result1");
}, 200);
});
},
doTask2() {
return new Promise(resolve => {
log("task2 started");
setTimeout(() => {
log("task2 complete");
resolve("result2");
}, 300);
});
},
doTask3() {
return new Promise(resolve => {
log("task3 started");
setTimeout(() => {
log("task3 complete");
resolve("result3");
}, 100);
});
},
};
log("Running the code");
server.doTask1()
.then(server.doTask2())
.then(server.doTask3())
.catch(() => log(`ERROR: ${error}`))
.then(() => log("Chain complete"));
log("Done running the code");
.as-console-wrapper {
max-height: 100% !important;
}
Notice how tasks 1-3 all start right away.
To make them run one after another, you need to pass a function, not a promise, into then
:
server.doTask1()
.then(() => server.doTask2())
// ^^^^^^
.then(() => server.doTask3())
// ^^^^^^
.catch( ... handle the error )
I've made two assumptions there:
- It matters what
this
doTask2
and doTask3
are called with
- You don't want the result from the previous task passed to the next one.
There are other ways you could write it if either or both of those isn't true.
Live Example:
const start = Date.now();
const elapsed = () => String(Date.now() - start).padStart(4);
const log = msg => console.log(`[${elapsed()}] ${msg}`);
const server = {
doTask1() {
return new Promise(resolve => {
log("task1 started");
setTimeout(() => {
log("task1 complete");
resolve("result1");
}, 200);
});
},
doTask2() {
return new Promise(resolve => {
log("task2 started");
setTimeout(() => {
log("task2 complete");
resolve("result2");
}, 300);
});
},
doTask3() {
return new Promise(resolve => {
log("task3 started");
setTimeout(() => {
log("task3 complete");
resolve("result3");
}, 100);
});
},
};
log("Running the code");
server.doTask1()
.then(() => server.doTask2())
.then(() => server.doTask3())
.catch(() => log(`ERROR: ${error}`))
.then(() => log("Chain complete"));
log("Done running the code");
.as-console-wrapper {
max-height: 100% !important;
}
Alternatively, in an async
function:
try {
await server.doTask1();
await server.doTask2();
await server.doTask3();
// ...
} catch (error) {
// ...handle/report error...
}
Live Example:
const start = Date.now();
const elapsed = () => String(Date.now() - start).padStart(4);
const log = msg => console.log(`[${elapsed()}] ${msg}`);
const server = {
doTask1() {
return new Promise(resolve => {
log("task1 started");
setTimeout(() => {
log("task1 complete");
resolve("result1");
}, 200);
});
},
doTask2() {
return new Promise(resolve => {
log("task2 started");
setTimeout(() => {
log("task2 complete");
resolve("result2");
}, 300);
});
},
doTask3() {
return new Promise(resolve => {
log("task3 started");
setTimeout(() => {
log("task3 complete");
resolve("result3");
}, 100);
});
},
};
(async () => {
try {
await server.doTask1();
await server.doTask2();
await server.doTask3();
// ...
log("Chain complete");
} catch (error) {
// ...handle/report error...
log(`ERROR: ${error}`);
}
})();
.as-console-wrapper {
max-height: 100% !important;
}
Note: There are some non-standard libraries that don't start the asynchronous work promised until you call then
on the promise. Those are outliers. In the normal case, a promise is a completion token for work already in progress (and that's how async
functions behave).