【问题标题】:How to correctly handle a series of async calls inside a parent async call如何正确处理父异步调用中的一系列异步调用
【发布时间】:2020-03-01 15:14:00
【问题描述】:

我有一个用例,我想进行异步调用(认为它类似于 ajax),然后在该调用的成功块中,我想使用生成的 id 在循环中进行一系列异步调用家长电话。我的要求是:

  1. 我应该把显示成功的代码放在哪里?目前我把它放在成功块内的for循环之后,但它有一个问题,它会在子异步调用完成之前被执行,因为for循环不会等待调用并且会立即执行并且代码会去祝酒成功。
  2. 如果任何一个子调用失败,则不应发生进一步的调用(从效率的角度来看这更重要),而且在这种情况下,我应该能够删除创建的父记录,所以如何也要处理吗? 提前致谢!

示例代码sn-p:

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
    });

【问题讨论】:

    标签: javascript promise callback asynchronous-javascript


    【解决方案1】:

    因为听起来您想连续运行asyncCallB(),以便在其中一个失败时避免任何额外的调用,那么使用async/await 最容易实现。

    为此,您必须将包含函数标记为async,以便您可以使用await。然后,您可以使用await 对您的异步操作进行排序:

    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
        }
    }
    

    为了可能更快的性能,您可以并行运行asyncCallB() 操作,如下所示,但所有asyncCallB() 调用都将运行,即使第一个调用有错误(因为它们都是并行启动的):

    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
        }
    }
    

    【讨论】:

    • 这正是我想要的。非常感谢。我只有一个疑问,您没有在 asyncCallA 方法前面使用 await 。是故意的吗?查看代码,似乎在执行以下行之前它不会等待 asyncCallA 调用完成。如果我错了,请纠正我。
    • @dj_1993 - 是的,这是一个错字。现已修复。
    【解决方案2】:
    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
    });
    

    【讨论】:

    • 感谢您的回答。但我找不到 asyncCallB 在任何地方使用。你是如何在 Promise 中使用 asyncCallB 调用的?
    【解决方案3】:
    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
        });
    

    【讨论】:

    • 我认为这个解决方案将解决我的问题的第一部分,但我如何解决第二部分?一旦其中任何一个失败,我不希望子调用继续被执行。我该如何做到这一点?
    【解决方案4】:

    确保异步链接发生的最佳选择是使用 array.reduce 函数,下面是相同的示例代码。

    如果你不清楚 array.reduce 和 promise 是如何工作的。我建议你参考这篇文章。

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

    下面是示例代码,您可以使用。

    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
        });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-06-10
      • 2011-09-18
      • 1970-01-01
      • 2015-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多