做者:Dunkjavascript
翻譯:瘋狂的技術宅前端
原文:nikodunk.com/how-to-chai…java
未經容許嚴禁轉載數據庫
在我完成 electrade 的工做之餘,還幫助一個朋友的團隊完成了他們的項目。最近,咱們但願爲這個項目構建一個 Craiglist 風格的匿名電子郵件中繼,其中包含 「serverless」 Google Firebase Function(與 AWS Lambda,Azure Function 等相同)。到目前爲止,我發現用 .then()
回調處理異步操做更容易思考,可是我想在這裏用 async/await,由於它讀起來更清晰。我發現大多數關於連接多個函數的文章都沒有用,由於他們傾向於發佈從MSDN 複製粘貼的不完整的演示代碼。在 async/await 上有一些難以調試的陷阱,由於我遇到了全部這些陷阱,因此我將在這裏發佈本身的完整代碼並解釋個人學習過程。前端工程化
這是鏈接多個函數的工做代碼,等待解決全部問題,而後 then 發送結果。主要錯誤是:promise
每一個 async function myFunction(){ <your code here> }
聲明自動將整個異步函數的代碼(即 <your code here>
)包裝在 new Promise
中,而後轉換爲 return x
並在代碼中加入 resolve(x)
。 可是你還須要在它以外等待(即 let y = await myFunction()
)或它實際上不會等待。這個調試是很是煩人的。app
在雲函數中,你必須發送帶有 res.send()
的響應,不然函數會認爲它失敗並從新運行它。less
下面的代碼要作這些事情:異步
咱們有 2 個正常的同步函數 getFieldsFromRequest()
和 extractCourseIdFromEmailAddress()
—— 這裏沒問題。async
而後咱們須要 async
函數 getEmailOfCourseWithCourseId()
從Firestore獲取課程的電子郵件地址。咱們不知道從 Firestore 獲取內容須要多長時間,所以它是 async
的,咱們須要運行接下來的兩個函數並返回(或以 promise 解析)courseEmail
。
接下來的兩個函數 saveToCloudFirestore()
和 sendEmailInSendgrid()
,不能在 getEmailOfCourseWithCourseId()
以前運行並返回 courseEmail
,不然它們將認爲 courseEmail
未定義,這樣的話一切都變得糟透了。 經過 awaiting 上面的函數 getEmailOfCourseWithCourseId() 並傳遞 courseEmail,這些函數(以及 if 運算符)將等到這種狀況發生(也就是說已經解決),而後運再行。
最後,在運行 saveToCloudFirestore()
和 sendEmailInSendgrid()
並返回它們的值以前,不能發送 res.send()
,不然咱們的整個雲函數將在工做完成以前中斷。爲此,咱們將 saveToCloudFireStore()
和 sendEmailInSendgrid()
響應(它們返回的內容)保存到變量中,其惟一目的是標記上述函數什麼時候完成。這在某種意義上取代了 .then()
:它等待這兩個變量( savedToCloud
和 sentEmail
)「到達」(他們的 Promise 已經解決),而後運行 res.send)()
。
爲了便於閱讀,我已經刪除了你應該在實踐中進行的 try/catch 包裝。你永遠不該該捕獲錯誤,但刪除它們會使 async/await 概念更容易理解。
// this is the cloud function you can call over HTTP. It is basically for email relay:
// it gets an email from sendgrid, parses the fields, looks up the real email with the courseId,
// saves to FireStore and sends and email with sendgrid.
// Finally, it sends a res.send() to end the cloud function
// {* import a bunch of shit *}
// main function
exports.emailFunction = functions.https.onRequest(async (req, res) => {
let fields = getFieldsFromRequest(req); // sync
let courseId = extractCourseIdFromEmailAddress(fields); // sync
let courseEmail = await getEmailOfCourseWithCourseId(courseId); // async
let savedToCloud = await saveToCloudFirestore(fields, courseEmail, courseId); // async
let sentEmail = await sendEmailWithSendgrid(fields, courseEmail); // async
res.status(200).send(savedToCloud, sentEmail); // Once sentEmail and saveToCloud have been returned (aka promises have been resolved, aka their functions have been run), res.send() will run so Firebase/SendGrid know that func worked.
});
// Helper functions below
function getFieldsFromRequest(req) { // sync
let fields = readTheFieldsFromReqWithBusboy(req)
return fields;
}
function extractCourseIdFromEmailAddress(fields) { // sync
let courseId = fields.to.substring(0, fields.to.indexOf('@'));
return courseId;
}
async function getEmailOfCourseWithCourseId(courseId) { // async important
let courseData = await database.get(courseId)
let courseEmail = courseData.email;
return courseEmail; // due to function being labeled async above, this is the equivalent of wrapping the whole function in 'return new Promise(resolve) => {}' and then returning a 'resolve(result)'
}
async function sendEmailWithSendgrid(fields, courseEmail) { // async important
let msg = {to: courseEmail, from: fields.from, text: fields.text}
let sentEmail = await sendgrid.send(msg)
return sentEmail; // due to function being labeled async above, this is the equivalent of wrapping the whole function in 'return new Promise(resolve) => {}' and then returning a 'resolve(result)'
}
async function saveToCloudFirestore(fields, courseEmail, courseId) { // async important
let savedToCloud = await database.add(fields, courseEmail, courseId)
return savedToCloud;
}
複製代碼
最後用 try {}catch {}
包裝最後3個異步函數和主函數來捕獲錯誤。此外,數據庫代碼不能原封不動的複製 —— 它僅用於說明目的!