【问题标题】:Node.js: Unable to return new array from array.map()Node.js:无法从 array.map() 返回新数组
【发布时间】:2018-06-18 20:16:16
【问题描述】:

我正在使用一个名为 Okrabyte 的包从文件夹中的每个图像文件中提取单词。结果应该是一个新数组,其中包含我可以在其他函数中使用的提取文本。

当我运行这个时:

var fs = require("fs");
var okrabyte = require("okrabyte");


fs.readdir("imgs/", function(err, files){
  files.map((file)=>{
    okrabyte.decodeBuffer(fs.readFileSync("imgs/"+ file), (err, data)=>{
      let splitWords = data.split(" ");
      let word = splitWords[0].substr(1);
      console.log(word);
    })
  })
})

控制台记录每个单词。要返回包含这些单词的数组,我尝试了以下方法:

async function words() {
    await fs.readdir("imgs/", function (err, files) {
        return files.map(async (file) => {
            await okrabyte.decodeBuffer(fs.readFileSync("imgs/" + file), async (err, data) => {
                let splitWords = data.split(" ");
                let word = splitWords[0].substr(1);
                return word
            })
        })
    })
}

var testing = await words();

console.log(testing);

这给了undefined 我试过把所有东西都变成一个承诺,我试过 async-await,我试过把每个单词推入一个新数组并在闭包中返回该数组,但没有任何效果 - 我是什么做错了吗??

【问题讨论】:

    标签: javascript arrays node.js async-await fs


    【解决方案1】:

    如果您的 map 函数是异步的,那么它会返回一个 Promise,因此您的映射数组实际上是一个 Promise 数组。但是您可以使用Promise.all 来获取该数组的解析值。

    此外,您正在尝试等待对fs.readdirokrabyte.decodeBuffer 的调用,它们都接受回调并且返回承诺。因此,如果你想在那里使用 Promise,你必须手动将它们包装在 Promise 构造函数中。

    我会这样做:

    async function words() {
        // Wrap `fs` call into a promise, so we can await it:
        const files = await new Promise((resolve, reject) => {
            fs.readdir("imgs/", (err, files) => { err ? reject(err) : resolve(files); });
        });
    
        // Since map function returns a promise, we wrap into a Promise.all:
        const mapped = await Promise.all(files.map((file) => {
            // Wrap okrabyte.decodeBuffer into promise, and return it:
            return new Promise((resolve, reject) => {
                okrabyte.decodeBuffer(fs.readFileSync("imgs/" + file), (err, data) => {
                    if (err) return reject(err);
                    const splitWords = data.split(" ");
                    const word = splitWords[0].substr(1);
                    resolve(word);
                })
            })
        }))
    
        // Mapped is now an array containing each "word".
        return mapped;
    }
    
    var testing = await words();
    
    // Should now log your array of words correctly.
    console.log(testing);
    

    【讨论】:

    • 非常简洁的解决方案/解释 - 谢谢!
    【解决方案2】:

    您不应该那样使用 async-await。在处理 Promise 时应该使用它。库 okrabyte 使用了回调的概念。

    我建议你遵循这种方法:

    (1) 将 okrabyte.decodeBuffer 部分包含在一个函数中,该函数返回一个在回调中解析的承诺。

    (2) 使用 files.map 生成一个调用你在 (1) 中定义的函数的 Promise 数组

    (3) 使用 Promise.all 等待所有 promise 执行并完成,然后再继续处理所有单词。

    演练:

    第 1 部分

    const processWord = (file) => {
      return new Promise((resolve, reject) => {
        okrabyte.decodeBuffer(fs.readFileSync("imgs/"+ file), (err, data)=>{
          if (err) {
            reject(err); // <--- reject the promise if there was an error
            return;
          }
    
          let splitWords = data.split(" ");
          let word = splitWords[0].substr(1);
          resolve(word); // <--- resolve the promise with the word
        })
      });
    }
    

    你创建了一个函数,将解码部分包装成一个最终用单词解析的承诺(或因错误而被拒绝)。

    第 2 部分

    const promises = files.map((file)=>{
      return processWord(file);
    })
    

    上面会生成一个promise数组。

    第 3 部分

    fs.readdir("imgs/", function(err, files){
      const promises = files.map((file)=>{
        return processWord(file);
      })
    
      Promise.all(promises)
        .then(responses => {
          // responses holds a list of words
          // You will get each word accessing responses[0], responses[1], responses[2], ...
          console.log(responses);
        })
        .catch(error => {
          console.log(error); // Deal with the error in some way
        });
    })
    

    上面使用 Promise.all 等待所有的 Promise 解决,然后再转到 then() 块,假设没有发生错误。

    您可以在一个方法中进一步隔离上面的构造,该方法将返回一个带有所有单词列表的 Promise,这与第 1 部分中的 processWord 函数中所做的方式大致相同。这样,您最终可以使用 async -await 如果你愿意,而不是在 then() 块中处理事情:

    const processEverything = () => {
      return new Promise((resolve, reject) => {
    
        fs.readdir("imgs/", function(err, files){
          const promises = files.map((file)=>{
            return processWord(file);
          })
    
          Promise.all(promises)
            .then(responses => {
              resolve(responses);
            })
            .catch(error => {
              reject(error);
            });
        })
      });
    };
    
    const words = await processEverything();
    console.log(words);
    

    【讨论】:

    • 这也是一个非常棒的演练谢谢!我选择了另一个答案,因为它简单明了。
    【解决方案3】:

    您将 word 值返回给封闭的函数,而不是 map() 函数。

    希望这段代码对你有所帮助。

    async function words() {
        global.words = [];
        await fs.readdir("imgs/", function(err, files){
          return files.map( async(file)=>{
            await okrabyte.decodeBuffer(fs.readFileSync("imgs/"+ file), async (err, data)=>{
              let splitWords = data.split(" ");
              let word = splitWords[0].substr(1);
              global.words.push(word);
            })
          })
        })
      }
    
      var testing = await words();
      testing = global.words;
      console.log(testing);
    

    【讨论】:

    • 不幸的是,这不起作用,因为我们使用的是 map,所以我想利用 map() 的结果数组,而不是创建一个额外的数组并将元素推送到它。
    猜你喜欢
    • 2015-09-23
    • 1970-01-01
    • 2020-08-19
    • 1970-01-01
    • 2017-08-20
    • 1970-01-01
    • 2019-09-09
    • 2019-09-02
    • 2021-05-27
    相关资源
    最近更新 更多