【问题标题】:NodeJS constructing array from asynchronious callbacks before returningNodeJS在返回之前从异步回调构造数组
【发布时间】:2017-08-29 21:45:40
【问题描述】:

我正在编写一个返回值和数组的函数。一些值是在回调中计算的。但是我不知道如何使程序异步,所以我所有的结果都在数组中,并且在返回后没有添加。

let array = [] 
for (stuff : stuffs) {
  if (condition) {
     array.add(stuff)
  } else {
     api.compute(stuff, callback(resp) {
          array.add(resp.stuff)
     }
  }
}
res.json({ "stuff": array })

在此示例中,数组在异步调用完成之前写入响应。

如何使这项工作异步进行?

【问题讨论】:

    标签: node.js express asynchronous


    【解决方案1】:

    您必须使用其中一种方法:

    • 异步库
    • Promise.all
    • 协程/生成器
    • 异步/等待

    我认为最酷的是async/await。首先我们修改你的函数,让它返回一个promise:

    const compute = function(stuff) {
      return new Promise( (resolve, reject) => {
        api.compute(stuff, callback(resp){
          resolve(resp.stuff)
        });
      });
    };
    

    然后我们使用异步处理程序修改您的路由:

    app.get('/', async function(req, res, next) {
      const array = [];
      for (const stuff of stuffs) {
        if (condition) {
          array.add(stuff);
        } else {
          const stuff = await compute(stuff);
          array.push(stuff);
        }
      }
      res.json({ stuff: array });
    });
    

    注意:您可能需要将节点版本更新到最新版本。

    更新:

    那些不知道事件循环如何工作的人,执行这个sn-p,并完成它:

    const sleep = async function(ms) {
      console.log(`Sleeping ${ms}ms`);
      return new Promise( resolve => setTimeout(resolve, ms));
    };
    
    async function job() {
      console.log('start');
    
      for (let t = 0; t < 10; t++) {
        await sleep(100);
      }
    }
    
    job();
    
    console.log('oops did not expect that oO');
    

    你会感到惊讶。

    【讨论】:

    • 将异步代码放入循环中是一种不好的做法。你应该创建一个 promise 数组,然后 Promise.all 他们。 (同意异步很棒!)
    • 和瀑布一样,有什么不好的?
    • 我同意@GrégoryNEUT,在处理异步代码时应避免循环。
    • 谁能解释为什么应该避免它?
    • @GrégoryNEUT 我认为你在这里使用了错误的提及
    【解决方案2】:

    这是一个没有使用回调的包的答案

    创建一个递归处理所有东西的函数。

    getArray(stuffs, callback, index = 0, array = []) {
      // Did we treat all stuffs?
      if (stuffs.length >= index) {
        return callback(array);
      }
    
      // Treat one stuff
      if (condition) {
        array.add(stuffs[index]);
    
        // Call next
        return getArray(stuffs, callback, index + 1, array);
      }
    
      // Get a stuff asynchronously
      return api.compute(stuffs[index], (resp) => {
        array.add(resp.stuff);
    
        // Call next
        return getArray(stuffs, callback, index + 1, array);
      });
    }
    

    怎么称呼?

    getArray(stuffs, (array) => {
       // Here you have your array
       // ...
    });
    

    编辑:更多解释


    我们想要将您的循环转换为处理异步函数调用的循环。

    目的是一个getArray 调用将处理您的stuffs 数组的一个索引。

    处理完一个索引后,函数会再次调用自己来处理下一个索引,直到全部处理完毕。

    -> Treat index 0 -> Treat index 1 -> Treat index 2 -> Return all result
    

    我们正在使用参数通过流程传递信息。 Index 知道我们必须处理哪个数组部分,array 保留我们计算过的部分。


    编辑:改进 100% 异步解决方案


    我们在这里所做的是将您的初始 for 循环简单地转换为异步代码。可以通过使其完全异步来改进它,这使它变得更好但稍微困难一些。

    例如:

    // Where we store the results
    const array = [];
    
    const calculationIsDone = (array) => {
      // Here our calculation is done
      // ---
    };
    
    // Function that's gonna aggregate the results coming asynchronously
    // When we did gather all results, we call a function
    const gatherCalculResult = (newResult) => {
      array.push(newResult);
    
      if (array.length === stuffs.length) {
        callback(array);
      }
    };
    
    // Function that makes the calculation for one stuff
    const makeCalculation = (oneStuff) => {
      if (condition) {
        return gatherCalculResult(oneStuff);
      }
    
      // Get a stuff asynchronously
      return api.compute(oneStuff, (resp) => {
        gatherCalculResult(resp.stuff);
      });
    };
    
    // We trigger all calculation
    stuffs.forEach(x => x.makeCalculation(x));
    

    【讨论】:

    • 你能解释一下这背后的想法吗?它是否有效,因为最后一个返回块?那为什么不以其他方式阻止呢?
    猜你喜欢
    • 1970-01-01
    • 2014-11-26
    • 1970-01-01
    • 1970-01-01
    • 2015-04-21
    • 2021-07-21
    • 2017-05-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多