【问题标题】:node.js: how to use sequence and promise to model two aync loopsnode.js:如何使用序列和承诺来建模两个异步循环
【发布时间】:2018-10-02 02:46:41
【问题描述】:

我在node.js中有两个函数,我们称它们为func_A和func_B,它们都需要循环调用,但需要一个接一个地调用..

首先,func_a 需要循环调用 num1 次。

for (i=0; i<num1; i++)
{
    func_a(i, function(err,cb1))
}

上述完成后,func_b需要循环调用num2次

for (j=0; j<num2; j++)
{
    func_b(j, function(err,cb2))
}

当所有上述 aync 函数调用完成并返回时,我需要对两个 cb 结果执行其他操作。我可以使用地狱般的回调金字塔来完成上述操作,并使用计数器来跟踪回调完成。但我想使用序列和承诺来简化我的代码。我该怎么做?我不太清楚如何使用循环调用的函数来完成它。

【问题讨论】:

    标签: javascript node.js asynchronous promise sequence


    【解决方案1】:

    在制作func_a()func_b() 的promisified 版本后,您可以使用Promise.all()awaitasync function 中不使用计数器的聚合结果:

    const promisify = fn => function () {
      return new Promise((resolve, reject) => {
        // forward context and arguments of call
        fn.call(this, ...arguments, (error, result) => {
          if (error) {
            reject(error)
          } else {
            resolve(result)
          }
        })
      })
    }
    
    const func_a_promisified = promisify(func_a)
    const func_b_promisified = promisify(func_b)
    
    async function loop_a_b (num1, num2) {
      // await pauses execution until all asynchronous callbacks have been invoked
      const results_a = await Promise.all(
        Array.from(Array(num1).keys()).map(i => func_a_promisified(i))
      )
    
      const results_b = await Promise.all(
        Array.from(Array(num2).keys()).map(j => func_b_promisified(j))
      )
    
      return {
        a: results_a,
        b: results_b
      }
    }
    
    // usage
    loop_a_b(3, 4).then(({ a, b }) => {
      // iff no errors encountered
      // a contains 3 results in order of i [0..2]
      // b contains 4 results in order of j [0..3]
    }).catch(error => {
      // error is first encountered error in chronological order of callback results
    })
    

    为了简化 Array.from(...).map(...) 混乱,您可以编写一个帮助程序 generator function 来同时调用异步函数:

    function * loop_fn_n (fn, n) {
      for (let i = 0; i < n; i++) {
        yield fn(n)
      }
    }
    

    然后将loop_a_b改为:

    async function loop_a_b (num1, num2) {
      // await pauses execution until all asynchronous callbacks have been invoked
      const results_a = await Promise.all(
        loop_fn_n(func_a_promisified, num1)
      )
    
      const results_b = await Promise.all(
        loop_fn_n(func_b_promisified, num2)
      )
    
      return {
        a: results_a,
        b: results_b
      }
    }
    

    正如@OleksiiTrekhleb 指出的那样,我在这里实现的promisify 函数也可以在Node.js 核心模块util 中使用。

    【讨论】:

      【解决方案2】:

      您可以使用 Promise 并将您的异步函数包装在其中。 我没有测试它,但逻辑应该可以工作

      let _func_a = (i) => {
                  return new Promise(function (resolve, reject) {
                      func_a(i, function (err, res) {
                          if (err) {
                              return reject(err);
                          }
                          return resolve(res);
                          })
                  })
          }   
          let _func_b = (j) => {
                  return new Promise(function (resolve, reject) {
                      func_b(j, function (err, res) {
                          if (err) {
                              return reject(err);
                          }
                          return resolve(res);
                          })
                  })
          }   
          (async function loop() {
              try {
                  for (i=0; i<num1; i++){
                      let a = await _func_a(i)
                  }
                  for (j=0; j<num2; j++){
                      let b = await _func_b(j)
                  }
              console.log("here you're sure both for loops are done")
              } catch(err) {
                  console.error("some error has occurred")
              }
          })();
      

      【讨论】:

      • 这不允许循环的迭代并行完成。
      • javascript 中没有并行,但我明白你的意思。如果作者确实需要同时运行 for 循环,一个解决方案可以不在 Promises 中包装 func_afunc_b,而是整个 for 循环然后使用相同的逻辑,或者 Promise.all
      • s/in parallel/concurrently
      【解决方案3】:

      你试过util.promisify(original)吗?

      您提到的代码可能会转换为:

      // Dependencies.
      const util = require('util');
      
      // Promisify your functions.
      const func_a_as_promise = util.promisify(func_a);
      const func_b_as_promise = util.promisify(func_b);
      
      // Now we may create 'async' function with 'await's.
      async function doSomething() {
        // Some data containers if you need any (?).
        const someDataA = [];
        const someDataB = [];
      
        // First async loop that looks like sync one.
        for (i=0; i < num1; i++){
          someDataA[i] = await func_a_as_promise(i);
        }
      
        // Second async loop that looks as sync one.
        for (j=0; j < num2; j++) {
          someDataB[j] = await func_b_as_promise(j);
        }
      
        // Do something with someDataA and someDataB here...
        // You'll get here after all async loops above are finished.
      }
      

      【讨论】:

      • 一般来说,如果代码不能直接在浏览器中运行,就不应该在栈sn-p中。我已为您将其转换为代码块。
      • 这和alfredopacino's answer有同样的问题。在每个单独的调用中使用 await 不允许循环中的异步函数同时工作,正如 OP 的问题中所要求的那样。
      猜你喜欢
      • 1970-01-01
      • 2015-10-09
      • 1970-01-01
      • 2017-10-30
      • 2018-01-23
      • 2013-01-21
      • 2020-11-22
      • 2017-07-18
      • 2017-10-22
      相关资源
      最近更新 更多