摘要:如果不希望定義多余的外層變量,則需要在鏈中的每一個函數(shù)中都返回變量,這樣做顯然更加糟糕。
譯者按: 通過真實的代碼示例感受Async/Await的力量。
原文: Async/await - A thorough example
譯者: Fundebug
為了保證可讀性,本文采用意譯而非直譯。另外,本文版權(quán)歸原作者所有,翻譯僅用于學(xué)習(xí)。
既然Node.js 8已經(jīng)LTS了,我想大家是時候試一試Async/Await特性了,真的很好用!它可以幫助我們用同步的方式寫異步代碼,極大地提高了代碼的可讀性。在過去的2年時間里,Promise給我們帶來了不少便利,同時也讓我們有一些失望。
這邊博客,我將介紹一個真實的代碼示例,它是一個REST API的controller。通過展示我們?nèi)绾螐腜romise切換到async/await,你講能夠體會到Async/Await的神奇之處!
Promise示例下面是我的工作項目中真實的Controller代碼:
const BPromise = require("bluebird"); const { WrongCredentialsError, DBConnectionError, EmailError } = require("./../errors"); /** * Emulate an Express.js route call as an example */ loginController({}, { json: response => console.log(response) }, null) function loginController (req, res, err) { const { email, password } = req; let user; BPromise.try(() => validateUserInput(req)) .then(() => fetchUserByEmail(email)) .then(fetchedUser => user = fetchedUser) .then(() => comparePasswords(req.password, user.password)) .then(() => markLoggedInTimestamp(user.userId)) .then(() => sendEmail(user.userId)) .then(() => generateJWT(user)) .then(token => res.json({ success: true, token })) .catch(WrongCredentialsError, () => res.json({ success: false, error: "Invalid email and/or password" })) .catch(EmailError, DBConnectionError, () => res.json({ success: false, error: "Unexpected error, please try again" })) .catch(() => res.json({ success: false })) } /** * Validate input from Request * * @param {Object} input * @throws {WrongCredentialsError} * @returns {Void} */ function validateUserInput(input) { if (!input.email || !input.password) { throw new WrongCredentialsError(); } } /** * Fetch a User from the DB by Email * * @throws WrongCredentialsError * @throws DBConnectionError * @returns {BPromise} */ function fetchUserByEmail(email) { const user = { userId: "DUMMY_ID", email: "[email protected]", password: "DUMMY_PASSWORD_HASH" } return new BPromise(resolve => resolve(user)); } /** * Compare two password * * @param {String} inputPwd * @param {String} storedPwd * @throws {WrongCredentialsError} * @returns {Void} */ function comparePasswords(inputPwd, storedPwd) { if (hashPassword(inputPwd) !== storedPwd) { throw new WrongCredentialsError(); } } /** * Hash password * * @param {String} password * @returns {String} */ function hashPassword(password) { return password; } /** * Mark a user"s logged in timestamp * * @param {String} userId * @throws DBConnectionError * @returns {BPromise} */ function markLoggedInTimestamp(userId) { return new BPromise(resolve => resolve()); } /** * Send a follow up email * * @param {String} userId * @throws EmailError * @returns {BPromise} */ function sendEmail(userId) { return new BPromise(resolve => resolve()); } /** * Generate a JWT token to send to the client * * @param {Object} user * @returns {BPromise} */ function generateJWT(user) { const token = "DUMMY_JWT_TOKEN"; return new BPromise(resolve => resolve(token)); }
一些值得注意的要點:
多余的外層變量let user; /* ... */ .then(fetchedUser => user = fetchedUser) /* ... */ .then(() => sendEmail(user.userId)) /* ... */
可知,user是一個全局變量,因為我需要在Promise鏈中使用它。如果不希望定義多余的外層變量,則需要在Promise鏈中的每一個函數(shù)中都返回user變量,這樣做顯然更加糟糕。
煩人的Promise鏈/* ... */ BPromise.try(() => validateUserInput(req)) /* ... */
一個Promise鏈必須從Promise開始,但是validateUserInput函數(shù)并沒有返回Promise,這時需要使用Bluebird。我也知道這樣寫比較奇怪...
討厭的Bluebird我在很多地方都使用了Bluebird,如果不用它的話,代碼會更加臃腫。所謂DRY,即Don"t repeat yourself,我們可以使用Bluebird去盡量簡化代碼。但是,Bluebird是一個第三方依賴,如果出問題了怎么辦?去掉Bluebird應(yīng)該更好!
JavaScript太靈(gui)活(yi)了,出了BUG你也不知道,不妨接入Fundebug線上實時監(jiān)控。
Async/Await示例當(dāng)我放棄Promise,使用Async/Await之后,代碼是這樣的:
const { WrongCredentialsError, DBConnectionError, EmailError } = require("./../errors"); /** * Emulate an Express.js route call as an example */ loginController({}, { json: response => console.log(response) }, null) /** * * @param {Object} req * @param {Object} res * @param {Object} err * @returns {Void} */ async function loginController(req, res, err) { const { email, password } = req.email; try { if (!email || !password) { throw new WrongCredentialsError(); } const user = await fetchUserByEmail(email); if (user.password !== hashPassword(req.password)) { throw new WrongCredentialsError(); } await markLoggedInTimestamp(user.userId); await sendEmail(user.userId); const token = await generateJWT(user); res.json({ success: true, token }); } catch (err) { if (err instanceof WrongCredentialsError) { res.json({ success: false, error: "Invalid email and/or password" }) } else if (err instanceof DBConnectionError || err instanceof EmailError) { res.json({ success: false, error: "Unexpected error, please try again" }); } else { res.json({ success: false }) } } } /** * Fetch a User from the DB by Email * * @throws WrongCredentialsError * @throws DBConnectionError * @returns {Promise} */ function fetchUserByEmail(email) { const user = { userId: "DUMMY_ID", email: "[email protected]", password: "DUMMY_PASSWORD_HASH" } return new Promise(resolve => resolve(user)); } /** * Hash password * * @param {String} password * @returns {String} */ function hashPassword(password) { return password; } /** * Mark a user"s logged in timestamp * * @param {String} userId * @throws DBConnectionError * @returns {Promise} */ function markLoggedInTimestamp(userId) { return new Promise(resolve => resolve()); } /** * Send a follow up email * * @param {String} userId * @throws EmailError * @returns {Promise} */ function sendEmail(userId) { return new Promise(resolve => resolve()); } /** * Generate a JWT token to send to the client * * @param {Object} user * @returns {Promise} */ function generateJWT(user) { const token = "DUMMY_JWT_TOKEN"; return new Promise(resolve => resolve(token)); }
哈哈!?。?/p> 沒有外層變量
現(xiàn)在,所有函數(shù)都在同一個作用域中調(diào)用,不再需要.then函數(shù)。因此,我們不再需要定義多余的全局變量,也不需要做多余的變量賦值。
沒有多余的函數(shù)Promise示例中的同步函數(shù)validateInput和comparePasswords的代碼可以與異步函數(shù)寫在一起,因此可以不再需要定義多帶帶的函數(shù),代碼更少。
可讀性更高異步代碼采用同步方式來寫,同時減少了代碼量,可讀性大大提高。
不再需要Bluebird原生的Promise可以替代Bluebird,且不再需要Bluebird的try方法了。
結(jié)論作為程序員,我們應(yīng)該努力完善代碼。Async/Await可以帶來很大好處,幫助我們寫出可讀性更高的代碼。如果你堅持使用Promise,不妨看看如何在Promise鏈中共享變量?。
如果你對Async/Await感興趣的話,可以看看這些博客:
重構(gòu):從Promise到Async/Await
Async/Await替代Promise的6個理由
Async/Await是這樣簡化JavaScript代碼的
版權(quán)聲明:
轉(zhuǎn)載時請注明作者Fundebug以及本文地址:
https://blog.fundebug.com/2018/01/31/a-real-async-await-example/
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/107433.html
摘要:版本以及之前,本身還沒有異步執(zhí)行代碼的能力,宿主環(huán)境傳遞給引擎,然后按順序執(zhí)行,由宿主發(fā)起任務(wù)。采納引擎術(shù)語,把宿主發(fā)起的任務(wù)稱為宏觀任務(wù),把引擎發(fā)起的任務(wù)稱為微觀任務(wù)?;居梅ㄊ纠幕卣{(diào)是一個異步的執(zhí)行過程。 筆記說明 重學(xué)前端是程劭非(winter)【前手機(jī)淘寶前端負(fù)責(zé)人】在極客時間開的一個專欄,每天10分鐘,重構(gòu)你的前端知識體系,筆者主要整理學(xué)習(xí)過程的一些要點筆記以及感悟,完整的...
摘要:版本以及之前,本身還沒有異步執(zhí)行代碼的能力,宿主環(huán)境傳遞給引擎,然后按順序執(zhí)行,由宿主發(fā)起任務(wù)。采納引擎術(shù)語,把宿主發(fā)起的任務(wù)稱為宏觀任務(wù),把引擎發(fā)起的任務(wù)稱為微觀任務(wù)?;居梅ㄊ纠幕卣{(diào)是一個異步的執(zhí)行過程。 筆記說明 重學(xué)前端是程劭非(winter)【前手機(jī)淘寶前端負(fù)責(zé)人】在極客時間開的一個專欄,每天10分鐘,重構(gòu)你的前端知識體系,筆者主要整理學(xué)習(xí)過程的一些要點筆記以及感悟,完整的...
摘要:版本以及之前,本身還沒有異步執(zhí)行代碼的能力,宿主環(huán)境傳遞給引擎,然后按順序執(zhí)行,由宿主發(fā)起任務(wù)。采納引擎術(shù)語,把宿主發(fā)起的任務(wù)稱為宏觀任務(wù),把引擎發(fā)起的任務(wù)稱為微觀任務(wù)?;居梅ㄊ纠幕卣{(diào)是一個異步的執(zhí)行過程。 筆記說明 重學(xué)前端是程劭非(winter)【前手機(jī)淘寶前端負(fù)責(zé)人】在極客時間開的一個專欄,每天10分鐘,重構(gòu)你的前端知識體系,筆者主要整理學(xué)習(xí)過程的一些要點筆記以及感悟,完整的...
摘要:一方面,這里替代的是異步代碼的編寫方式,并非完全拋棄大家心愛的,地球人都知道是基于的,不用太傷心另一方面,是基于回調(diào)函數(shù)實現(xiàn)的,那也沒有替代回調(diào)函數(shù)咯重構(gòu)代碼之后,我仍然用到了庫。 摘要: 夸張點說,技術(shù)的發(fā)展與歷史一樣,順之者昌,逆之者亡。JS開發(fā)者們,趕緊擁抱Async/Await吧! GitHub倉庫: Fundebug/promise-asyncawait 早在半年多之前,...
閱讀 1128·2021-10-09 09:43
閱讀 18610·2021-09-22 15:52
閱讀 1072·2019-08-30 15:44
閱讀 3064·2019-08-30 15:44
閱讀 3254·2019-08-26 14:07
閱讀 915·2019-08-26 13:55
閱讀 2577·2019-08-26 13:41
閱讀 3096·2019-08-26 13:29