0

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
    });
dj_1993
  • 73
  • 7

4 Answers4

1

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
    }
}
jfriend00
  • 580,699
  • 78
  • 809
  • 825
  • That's exactly what I wanted. Thanks a lot. I have just one doubt, you didn't use await in front of asyncCallA method. Is it intentional? Looking at the code, it seems that it will not wait for asyncCallA call to be completed before executing below lines. Please correct me if I'm wrong. – dj_1993 Mar 01 '20 at 20:07
  • @dj_1993 - yes, that was a typo. Fixed now. – jfriend00 Mar 01 '20 at 23:14
0
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
});
Patrik Kullman
  • 371
  • 2
  • 4
  • Thanks for your answer. But I can't find asyncCallB getting used anywhere. How are you using asyncCallB calls inside the promises? – dj_1993 Mar 01 '20 at 15:33
0
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
    });
Palani samy
  • 107
  • 3
  • I think this solution will solve first part of my problem but how do I solve second part? I don't want child calls to continue getting executed as soon as any one of them fails. How do I achieve that? – dj_1993 Mar 01 '20 at 16:24
0

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
    });
Faiz Mohammed
  • 312
  • 2
  • 10