Thursday, February 10, 2022

JavaScript/TypeScript Promises

Summary - Two rules: Always return the Promise, always resolve or reject the Promise. If this was not enough, continue reading.

I’ve tried to understand how the asynchronous side of TypeScript and JavaScript works and how to avoid the problems related to it. My background is in development. I’ve been working with the concurrent systems. I’m used to locks, semaphores and all that stuff which are important to concurrent programming. But now I have to survive with the asynchronous JavaScript.

Most often my use case is that I read the data from somewhere, process it, and then do some new tricks with that data. So it’s much like the pipeline. You can’t process the data unless you’ve read it. You can’t do the new tricks for the data unless your processing has finished. You also have to be sure that you don’t exit before all data is handled.

The best way to manage this situation is to use Promise -class. Using it requires some understanding. I tried to google tutorials etc, but I always got lost. Rest of this blog article is for myself, but I hope it will help others. 

The basic structure for the promise is:

promise.then(...).then(...).then(...).catch(...).finally(...)

To get that chain working properly we have to take a look at the real code. The code is simple: I have the array of numbers. I want to wait that many seconds for each “processor”. First we write to the console when we come to the processor. Then we write when we come out from the processor. And at the end we display: "Goodbye” for the developer.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
const array1 = [9, 4, 6, 7, 6, 8];
 
async function testfunc(test: string, tst: number) {
 console.log(`In ${test}`);
 return new Promise((res, rej) => {
   setTimeout(res, tst * 1000);
 }).then(() => {
   console.log(`Out ${test}`);
   return new Promise((res) => {
     res(1);
   });
 });
}
 
const testidata = array1.map((item, index) =>
 testfunc(`Number ${index}`, item)
);
 
Promise.all(testidata).then(() => {
 console.log("Goodbye");
});

There’s two rules to create the promise chain which really works.

Rule number one: The promise must ALWAYS be resolved or rejected. The resolve and reject functions are parameters of the promise parameter function. You see this at the lines 5 and 9. 

Rule number two: Always return the Promise if you want the Promise chain to continue. This is shown in line 9. 

Promise.all() blocks until all promises on the list are resolved successfully or exception is thrown. Promise.allSettled() blocks until all promises are resolved. It’s easy to test other cases if you just remember those two rules above.

Hopefully this helps others also. I hope I’ll end up on this page when I next have to start wondering how the Promise works.