DEV Community

John Peters
John Peters

Posted on • Updated on

Getting Multiple Promises to Behave

Problem
"A Program has a routine which has multiple Promises all resolving at different times. The current routine must be completely finished with all Asynchronous work prior to starting the next step. How do we accomplish this?"

function ExploringMultipleResolves() {

    var p = new Promise((resolve, reject) => { 
        setTimeout(() => { resolve("p"); }, 5000) })
         .then(() => {
              //is resolved after 5 seconds
        }
   );

    var q = new Promise((resolve, reject) => { 
         resolve("q"); })
         .then(() => {
             //immediate resolve
    });

    var arr = [].concat(p).concat(q);
    Promise.all(arr).then(
        done => {
            //Only called when all promises in arr are resolved.
            CallNextStep();
        });
}
Enter fullscreen mode Exit fullscreen mode

In the code above we have two Promises, p and q; p finishes 5 seconds later than q. We don't want CallNextStep to execute prior to all other Promises being resolved. The done function (a callback), on the Promise.all does the job.

Promise.all

Promise.all will execute a callback after all the Promises it knows about are resolved.

var arr = [].concat(p).concat(q).concat(...);    
Promise.all(arr).then(
        done => {
            //Only called when all promises in arr are resolved.
            CallNextStep();
        });
Enter fullscreen mode Exit fullscreen mode

It takes an array of promises (p,q...), and will wait for the resolve of every item in that array, calling the then function to notify the program to move on to next thing.

Promise Queuing

As Javascript executes, each Promise is put onto the Promise Queue (wherever that lives). As soon as it's queued, the next statement executes. If there are multiple promises in a function, it's possible they all are put onto the queue before any of them complete. Depending on what they are doing, they will finish in some random order, that may not be the same random order each time it's run. This is totally contrary to synchronous programming!

Then

For Promises, the then function is a callback, telling us the Promise is resolved. Here's some links on the (Promise.Then) function. The call to the next asynchronous step is done within the callback, it looks exactly like the example above. The problem with chaining multiple then calls is you wind up with "callback hell"

Pyramid of Doom

Another term for callback hell is the Pyramid of Doom. Here we show what it looks like.

function MrAsync(){
  Asynch1()
    .then(()=>Async2
      .then(()=>Asynch3()
        .then(()=>Asynch1()
          .then(()=>"done"))
        )
      )
    )
 }
Enter fullscreen mode Exit fullscreen mode

Closures

One way to avoid callback hell is by use of the Promise Closure. Here's a typescript example of a Closure.

async function WorkAsync(): Promise<string> {
  return "Pretend this takes a long time...";
}

async function ClosureExample() {
  //this is the "Closure" pattern
  var ClosureData = await WorkAsync();
  CallNextMethod();
}
Enter fullscreen mode Exit fullscreen mode

The pattern above makes asynchronous code look like its synchronous! CallNextMethod will not execute prior to ClosureData being returned from the WorkAsync function.

How to Identify a Closure in Asynchronous programming

In Typescript, you will see the await statement preceding a call to an asynchronous function. The left side will set a variable, that's it!

//any 'captured' value such as Closure; is a Closure pattern.
let ClosureData = await WorkAsync();
Enter fullscreen mode Exit fullscreen mode

The "await" statement causes a suspension of execution, which resumes upon completion; setting the value of Closure shown above.

In C++ this is called a co-routine. (suspend and resume)

Not a Unique Problem

In Asynchronous programming, waiting for multiple callbacks before proceeding is not Unique to the Promise. All dependent Asynchronous requests must have a way of knowing when all dependencies are fulfilled. This is not a language issue, it's a functional issue. All Asynchronous implements I know about have ability to solve this problem.

Nodejs is asynchronous first; which, provides the cleanest code! In Nodejs, all we see is the (then) function call!

The Client Responsibility

Any time an Asynchronous interface is utilized, it's always the client's responsibility for error detection and correction. The client is the "controller" of the workflow and must be able to handle error conditions or retry logic. The overall workflow of the asynchronous application is the client side's responsibility.

Debugging Signatures

When converting to Asynchronous TypeScript/JavaScript execution, you will immediately collide with the problem of function calls returning out-of-order. It's simple to see with enough debug points and proper validation of your function parameters.

Parameters passed into a function will have only their initial value. A good reason to always initialize your variables and put validation logic on entry to all functions. Tracing the workflow will find subsequent work being logged after it was required.

A Major Debugging Tip

Send "flight recorder" data to the console. Upon entry to each function, log it's name and the proper number of tabs to reflect the order intended to run. The output should flow left to right with nothing out or order. Any output that is out of "tab" order, is your way to know the workflow isn't proper.

Conclusion

  • Asynchronous programming requires a rethinking of workflow, it's more complex than Synchronous styles.
  • Use Promise.all in any function that has multiple promises and downstream functions are dependent on that data.
  • Aviod callback hell, use Then carefully. -Be aware of dependencies. Place subsequent function calls with dependencies in places where the prior resolve is done. -Look into Closures of Promises.
  • The Asynchronous Client is always responsible for error detection and correction

On Deck: The Asynchronous nature of RXJS - Reactive Extensions for JavaScript.

JWP2017

Top comments (0)