假設一個 Express 路由呼叫 Mongoose 并且必須是異步的,以便它可以在 mongoose.find() 上等待。還假設我們正在接收 XML,但我們必須將其更改為 JSON,并且這也需要是異步的,以便我可以在其中呼叫 await。
如果我這樣做:
app.post('/ams', async (req, res) => {
try {
xml2js.parseString(xml, async (err, json) => {
if (err) {
throw new XMLException();
}
// assume many more clauses here that can throw exceptions
res.status(200);
res.send("Data saved")
});
} catch(err) {
if (err instanceof XML2JSException) {
res.status(400);
message = "Malformed XML error: " err;
res.send(message);
}
}
}
服務器永遠掛起。我假設 async/await 意味著服務器在某些事情結束之前遇到超時。
如果我把這個:
res.status(200);
res.send("Data saved")
在 catch() 之前的那一行,然后回傳,但它是每次回傳的唯一內容。即使拋出 XMLException,客戶端也會得到 200。
我可以在控制臺中看到 XMLException 拋出,但我無法得到 400 發回。我無法以將回應傳達給客戶端的方式執行任何捕獲塊。
有沒有辦法做到這一點?
uj5u.com熱心網友回復:
簡而言之,無法將錯誤從xml2js.parseString()回呼傳播到更高的代碼,因為該父函式已經退出并回傳。這就是普通回呼如何與異步代碼一起作業。
要理解這里的問題,您必須遵循xml2js.parseString()函式中的代碼流。如果你像這樣檢測它:
app.post('/ams', async (req, res) => {
try {
console.log("1");
xml2js.parseString(xml, async (err, json) => {
console.log("2");
if (err) {
throw new XMLException();
}
// assume many more clauses here that can throw exceptions
res.status(200);
res.send("Data saved")
});
console.log("3");
} catch (err) {
if (err instanceof XML2JSException) {
res.status(400);
message = "Malformed XML error: " err;
res.send(message);
}
}
console.log("4");
});
然后,您會在日志中看到:
1 // about to call xml2js.parseString()
3 // after the call to xml2js.parseString()
4 // function about to exit
2 // callback called last after function returned
在呼叫回呼之前,外部函式已完成并回傳。這是因為它xml2js.parseString()是異步和非阻塞的。這意味著呼叫它只是啟動操作,然后它立即回傳,其余函式繼續執行。它在后臺作業,一段時間后,它將一個事件發布到 Javascript 事件佇列,當解釋器完成它正在執行的任何其他操作時,它將接收該事件并呼叫回呼。
回呼將使用幾乎為空的呼叫堆疊進行呼叫。因此,您不能將傳統try/catch例外與這些簡單的異步回呼一起使用。相反,您必須在回呼中處理錯誤或從回呼中呼叫某個函式來為您處理錯誤。
當您嘗試在該普通的異步回呼中拋出時,例外只會回傳觸發異步操作完成的事件處理程式,因為呼叫堆疊中沒有其他內容。您try/catch在代碼中顯示的內容無法捕獲該例外。事實上,沒有其他代碼可以捕獲該例外——只有例外中的代碼。
這不是撰寫代碼的好方法,但是 nodejs 使用它生存了很多年(throw在這些情況下不使用)。然而,這就是發明 promises 的原因,當與新的語言特性一起使用時async/await,它們提供了一種更簡潔的做事方式。
而且,幸運的是在這種情況下xml2js.parseString()已經有一個 promise 介面。
所以,你可以這樣做:
app.post('/ams', async (req, res) => {
try {
// get the xml data from somewhere
const json = await xml2js.parseString(xml);
// do something with json here
res.send("Data saved");
} catch (err) {
console.log(err);
res.status(400).send("Malformed XML error: " err.message);
}
});
使用該xml2js.parseString()介面,如果您不向它傳遞回呼,它將回傳一個承諾,而不是決議為最終值或因錯誤而拒絕。這不是所有異步介面都可以做到的,但是如果介面最初具有舊樣式的回呼,然后他們現在想要支持承諾,那么這在當今相當普遍。較新的介面通常僅使用基于 Promise 的介面構建。無論如何,根據doc,如果您不傳遞回呼,此介面將回傳一個承諾。
You can then use await with that callback. If the promise resolves, the await will retrieve the resolved value of the promise. If the promise rejects, because you awaiting the rejection will be caught by the try/catch. FYI, you can also use .then() and .catch() with the promise, but in many cases, async and await are simpler so that's what I've shown here.
So, in this code, if there is invalid XML, then the promise that xml2js.parseString() returns will reject and control flow will go to the catch block where you can handle the error.
If you want to capture only the xml2js.parseString() error separately from other exceptions that could occur elsewhere in your code, you can put a try/catch around just it (though this code didn't show anything else that would likely throw an exception so I didn't add another try/catch). In fact, this form of try/catch can be used pretty much like you would normally use it with synchronous code. You can throw up to a higher level of try/catch too.
A few other notes, many people who first start programming with asynchronous operations try to just put await in front of anything asynchronous and hope that it solves their problem. await only does anything useful when you await a promise so your asynchronous function must return a promise that resolves/rejects when the asynchronous operation is complete for the await to do anything useful.
也可以采用一個沒有承諾介面的普通回呼異步函式,并在其周圍包裹一個承諾介面。您幾乎不想將 promise 介面函式與普通回呼異步操作混合使用,因為錯誤處理和傳播對于混合模型來說是一場噩夢。因此,有時您必須“承諾”較舊的界面,以便您可以使用承諾。在大多數情況下,您可以通過util.promisify()內置到 nodejs 中的 util 庫來做到這一點。幸運的是,由于 promises 和async/await是執行異步操作的現代且更簡單的方法,因此 nodejs 世界中大多數較新的異步介面都已經帶有 promise 介面。
uj5u.com熱心網友回復:
您在回呼函式中拋出例外。所以你不能指望路由器的catch塊來接收它。
處理此問題的一種方法是使用util.promisify。
try{
const util = require('util');
const parseString = util.promisify(xml2js.parseString);
let json = await parsestring(xml);
}catch(err)
{
...
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/399840.html
上一篇:獲取正文資料為空的帖子
下一篇:Robot.js非同步點擊按鍵
