dj_1993
dj_1993

Reputation: 113

How to correctly handle a series of async calls inside a parent async call

I have a use case where I want to make an async call (consider it similar to ajax) and then in success block of that call I want to make a series of async calls in a loop using the id which is generated by parent call. My requirements are :

  1. Where do I put the code of showing success toast? Currently I'm putting it after the for loop inside success block, but it has a problem that it will get executed before child async calls are completed because for loop will not wait for the calls and will get executed in no time and code will go to the showing of success toast.
  2. If any one of the child call fails, then no further calls should happen (this is more from an efficiency point of view), and also in such scenario, I should be able to delete the parent record which was created so how to handle that as well? Thanks in advance!

Sample code snippet :

asyncCallA(inputId)
    .then(output => {
        // inputIdsForChildCalls is the list of inputIds for child async 
        // calls
        inputIdsForChildCalls = [do something with output]
        for (let i = 0; i < inputIdsForChildCalls.length; i++) {
            asyncCallB(inputIdsForChildCalls[i])
                .then(output => {
                    // do something
                })
                .catch(error => {
                    // do something with error
                });
        }
        showSuccessToast("Records created successfully!");
    })
    .catch(error => {
        // do something with error
    });

Upvotes: 0

Views: 756

Answers (4)

jfriend00
jfriend00

Reputation: 707158

Since it sounds like you want to run asyncCallB() serially so you can avoid any additional calls if one of them fails, then this will be easiest to implement using async/await.

To do this, you will have to mark the containing function as async so you are allowed to use await. You can then use await to sequence your asynchronous operations:

async function someFunc(inputId) {
    try {
        let output = await asyncCallA(inputId);
        // inputIdsForChildCalls is the list of inputIds for child async 
        // calls
        let inputIdsForChildCalls = [do something with output]
        for (let childId of inputIdsForChildCalls) {
            let childResult = await asyncCallB(inputIdsForChildCalls[childId]);
            // process child result here
            // errors in asyncAllB() will have gone to the catch(e) statement below
        }
        showSuccessToast("Records created successfully!");
    } catch(e) {
        // handle error here
        // throw an error here if you want the caller to be able to see the error
    }
}

For possibly faster performance, you can run your asyncCallB() operations in parallel as shown below, but all asyncCallB() calls will be run, even if the first one has an error (since they are all launched in parallel):

async function someFunc() {
    try {
        let output = await asyncCallA(inputId);
        // inputIdsForChildCalls is the list of inputIds for child async 
        // calls
        let inputIdsForChildCalls = [do something with output]
        let allResults = await Promise.all(inputIdsForChildCalls.map(childId => {
            return asyncCallB(childId);
        }));
        // process allResults array here
        // errors will have gone to the catch(e) statement below
        showSuccessToast("Records created successfully!");
    } catch(e) {
        // handle error here
    }
}

Upvotes: 1

Faiz Mohammed
Faiz Mohammed

Reputation: 364

The best option to ensure that the async chaining happens is to use an array.reduce function below is the sample code for the same.

if you are not clear about how array.reduce and promise work. I would suggest you refer to this article.

https://developers.google.com/web/fundamentals/primers/promises

Below is the sample code, which you can use.

asyncCallA(inputId)
    .then(output => {
        // inputIdsForChildCalls is the list of inputIds for child async 
        // calls
        inputIdsForChildCalls = [];

        inputIdsForChildCalls.reduce(function(sequence, Id)
        {
            return sequence.then(function()
          {
             return asyncCallB(Id);
          }).then(function(asyncCallResult)
                {
              //do something
        });
        }, Promise.resolve())

    })
    .then(function()
    {
        showSuccessToast("Records created successfully!");
    })
    .catch(error => {
        // do something with error
    });

Upvotes: 0

Palani samy
Palani samy

Reputation: 113

asyncCallA(inputId)
    .then(output => {
        inputIdsForChildCalls = [do something with output]
        let syncCalls = [];
        for (let i = 0; i < inputIdsForChildCalls.length; i++) {
            syncCalls.push(asyncCallB(inputIdsForChildCalls[i]));
        }
        Promise.all(inputIdsForChildCalls)
           .then(outputs => {
            // do something
            showSuccessToast("Records created successfully!");
           })
        .catch(error => {
            // do something with error
        });
    })
    .catch(error => {
        // do something with error
    });

Upvotes: 0

Patrik Kullman
Patrik Kullman

Reputation: 951

asyncCallA(inputId)
.then(output => {
    inputIdsForChildCalls = [do something with output]
    Promise.all(inputIdsForChildCalls)
        .then(outputs => {
            // do something
            showSuccessToast("Records created successfully!");
        })
        .catch(error => {
            // do something with error
        });
    }
})
.catch(error => {
    // do something with error
});

Upvotes: 0

Related Questions