为什么我从涉及使用 Azure API 的 JavaScript 异步操作的 Promise.all 调用中得到 'undefined' 和空数组[]?

Why am I getting 'undefined' and empty array[] from a Promise.all call involving JavaScript async operations that use Azure APIs?

作为我使用 Azure AI APINode.js/Express, 的个人项目的一部分,我尝试使用 textkey phrases 响应 get 请求/viewText 路由从上传的 image/document.

中提取

下面是应该将该数据记录到控制台的代码:

app.get(`/viewText`, async (req, res) => {
  const answerReadResult = await readOperation(`${__dirname}\uploads\answer`);
  const markReadResult = await readOperation(`${__dirname}\uploads\mark`);
  
  Promise.all([
    readOperation(`${__dirname}\uploads\answer`),
    keyPhraseExtractor(answerReadResult),
    readOperation(`${__dirname}\uploads\mark`),
    keyPhraseExtractor(markReadResult),
  ]).then((data) => {
    console.log("data from Promise.all: ", data);
  });
});

我希望从 readOperation 函数接收到两个字符串,从 keyPhraseExtractor 接收到一个字符串数组,但我得到的只是:data from Promise.all: [ undefined, [], undefined, [] ]

这是 readOperation 函数的代码(它获取图像和 return 可识别文本作为字符串数组)

const readOperation = async (path) => {
  fs.readdir(path, (err, files) => {
    if (err) console.log(err);

    files.forEach(async (file) => {
      try {
        const results = await getTextFromImage(
          `${__dirname}\uploads\answer\${file}`
        );
        console.log("data from readOperation: ", results.join(" "));
        return results;
      } catch (err) {
        console.error(err);
      }
    });
  });
};

日志是:data from readOperation: you must be the change you want to see in the world !

N.B: 这个记录的数据是我试图从这个函数 return 得到的(没有 join(" ")) 但我得到 undefinedPromise.all

keyPhraseExtractor 函数(如下所示)需要一个字符串数组,并且应该 return 这些字符串中的关键短语(也作为一个字符串数组)

const keyPhraseExtractor = async (dataFromReadOperation) => {
  let results = dataFromReadOperation;
  try {
    const data = await keyPhraseExtraction(textAnalyticsClient, results);
    console.log("data inside keyPhraseExtractor: ", data);
    return data;
  } catch (err) {
    console.error(err);
  }
};

日志是data inside keyPhraseExtractor: []

readOperation 是错误的。在 async 函数中,您开始 readDir 并向其传递回调,但 async 函数中没有任何内容等待回调值(因此 readDir 可能未完成到 returns) 时,readOperation 中没有 return,所以绝对 returns undefined.

是的,您有 return results,但那是在 forEach async 箭头函数内。没有什么可以将其传递给 readOperation returns.

的封闭承诺
// This is not async, because it's returning a new Promise anyway.
const readOperation = /* NOT ASYNC */ (path) => {
  return new Promise((resolve, reject) => {
    fs.readdir(path, (err, files) => {
      if (err) {
        console.log(err);
        reject(err);       // Outer. Don't forget to stop here.
      }

      // Resolve the outer promise with an inner promise
      // from Promise.all, so getTextFromImage can run in
      // parallel. Also use `map` instead of `forEach` so the
      // return value is an array of Promises (due to the async).
      resolve(Promise.all(files.map(async (file) => {
        try {
          const results = await getTextFromImage(
            `${__dirname}\uploads\answer\${file}`
          );
          console.log("data from readOperation: ", results.join(" "));
          return results.join(" "); // each file's return should
                                    // be a string, right?
        } catch (err) {
          console.error(err);  // Inner.
          throw err;           // Re-throw so it rejects the promise.
        }
      })));
    });
  });
};

也就是说,我建议使用 fs Promises API,这样您就不会混合使用 err-first 回调 API 和 async 函数。

// This is async, because you can await the Promises inside.
const readOperation = async (path) => {
  let files;
  try {
    files = await fsPromises.readdir(path);
  } catch (err) {
    console.log(err);  // Outer.
    throw err;
  }
  // Still use Promise.all here, so the mapping can happen in parallel.
  // You could await this, but async functions that return Promises will
  // unwrap the results anyway: it only affects where rejections come from.
  return Promise.all(files.map(async (file) => {
    try {
      const results = await getTextFromImage(
        `${__dirname}\uploads\answer\${file}`
      );
      console.log("data from readOperation: ", results.join(" "));
      return results.join(" ");
    } catch (err) {
      console.error(err);  // Inner.
      throw err;           // Re-throw so it rejects the promise.
    }
  });
};