Node.js:无法从array.map()返回新数组

时间:2018-01-09 18:05:21

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

我正在使用名为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我试过把所有东西变成一个承诺,我试过异步等待,我已经尝试将每个单词推入一个新数组并将该数组返回到一个闭包但没有任何作用 - 什么我做错了吗?

3 个答案:

答案 0 :(得分:2)

如果你的map函数是异步的,那么它会返回一个promise,所以你映射的数组实际上是一个promises数组。但是,您可以使用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);

答案 1 :(得分:1)

你不应该使用async-await那样。当你处理承诺时应该使用它。库okrabyte使用回调的概念。

我建议您遵循这种方法:

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

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

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

操作实例:

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

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

第2部分

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

以上将生成一系列承诺。

第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在转到then()块之前等待所有promises解析,假设没有发生错误。

您可以在一个方法中进一步隔离上面的构造,该方法将返回带有所有单词列表的promise,与第1部分中processWord函数中完成的方式非常相似。这样,您最终可以使用async -awa如果你愿意,而不是处理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);

答案 2 :(得分:0)

您将字值返回到封闭函数但不返回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);