为什么我从涉及使用 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 API 和 Node.js/Express, 的个人项目的一部分,我尝试使用 text 和 key 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(" ")) 但我得到 undefined 从 Promise.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.
}
});
};
作为我使用 Azure AI API 和 Node.js/Express, 的个人项目的一部分,我尝试使用 text 和 key 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(" ")) 但我得到 undefined 从 Promise.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.
}
});
};