Node.js: 无法 return 来自 array.map() 的新数组

Node.js: Unable to return new array from array.map()

我正在使用一个名为 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);
    })
  })
})

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

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 我试过将一切变成一个承诺,我试过异步等待,我试过将每个单词推入一个新数组并 returning 该数组正在关闭但没有任何效果 - 我做错了什么??

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

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

我会这样做:

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

您正在将单词值返回给封闭函数而不是 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);

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

我建议您采用这种方法:

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

(2) 使用 files.map 生成调用您在 (1)

中定义的函数的承诺数组

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

你创建了一个函数,将解码部分包装成一个 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() 块之前等待所有承诺解决。

您可以在一个方法中进一步隔离上面的构造,该方法将 return 一个包含所有单词列表的承诺,这与第 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);