摘要:實(shí)例執(zhí)行以后,可以用方法分別指定狀態(tài)和狀態(tài)的回調(diào)函數(shù)。所以在使用處其中,函數(shù)接受回調(diào)函數(shù),作為對(duì)象的狀態(tài)變?yōu)闀r(shí)調(diào)用而回調(diào)函數(shù)作為對(duì)象的狀態(tài)變?yōu)闀r(shí)調(diào)用。
我最近正在看的一本書《聊聊架構(gòu)》,在進(jìn)入今天的主題之前,我想和大家分享這本書里的一個(gè)概念“生命周期”。
大致是這么說(shuō)的:
人類的生命很短,百年也只有短短的三萬(wàn)六千天。大部分人都不愿意接受一切都將消逝的事實(shí),總想活得更久,占有更多,享受更多。在人們短短的一生中,如何延長(zhǎng)自身的生命呢?一個(gè)辦法就是盡可能做出更多的成就,能夠讓更多的人生活得更好。在同樣的時(shí)間內(nèi)創(chuàng)造出更多的產(chǎn)出,相當(dāng)于把自己的生命延長(zhǎng)了。
其中有效的做法就是將每次活動(dòng)進(jìn)行拆分,自己執(zhí)行核心的【生命周期】,將【非核心生命周期】交給其它主體進(jìn)行。
比如,用戶購(gòu)物這一場(chǎng)景,從用戶進(jìn)入到商店,進(jìn)行瀏覽、詢問、購(gòu)買等活動(dòng),到離開商店,都是按時(shí)間順序一步一步在執(zhí)行的。對(duì)于我們來(lái)說(shuō),這其中的每一步都需要時(shí)間,對(duì)于現(xiàn)代人來(lái)說(shuō),太奢侈了。如果這時(shí)候,我們把這個(gè)購(gòu)物行為進(jìn)行拆分,把選購(gòu)交由別人執(zhí)行,由別人代替用戶上街選擇和過(guò)濾,或者通過(guò)網(wǎng)上推薦等來(lái)完成選購(gòu),用戶只需要最后確定物品的挑選即可。用戶的目的是買到自己需要的東西,而不是選購(gòu)本身。這樣就可以大大節(jié)省用戶的時(shí)間,將更多的精力放到其他核心事情上。
再比如,現(xiàn)在吃飯,需要自己到菜市場(chǎng)買菜、回來(lái)煮飯煮菜,吃完飯后,還需要自己洗碗等。有時(shí)候完全可以叫外賣的,肚子餓了,點(diǎn)點(diǎn)【餓了嗎】,接著就可以繼續(xù)做自己的事情了,坐等外賣送到。將【煮飯】這一耗時(shí)的事情交給別人來(lái)做。
在編程語(yǔ)言中,這就是同步與異步的區(qū)別。異步的作用就是將耗時(shí)的事情交給【別人】來(lái)做,自己繼續(xù)進(jìn)行;當(dāng)【別人】做完事情后,執(zhí)行回調(diào)函數(shù),帶回結(jié)果,交回自己執(zhí)行。
回調(diào)函數(shù)雖然 Javascipt 語(yǔ)言是“單線程”執(zhí)行環(huán)境,但在執(zhí)行模式下,分成同步和異步兩種模式,其中我們更多的使用回調(diào)函數(shù)的方式來(lái)進(jìn)行異步操作,如:
blogs.search = (words, res) => { const titleQuery = new AV.Query(Blog) titleQuery.contains("title", words); const descQuery = new AV.Query(Blog) descQuery.contains("desc", words); const tagsQuery = new AV.Query(Blog) tagsQuery.contains("tags", words); const wordsQuery = AV.Query.or(titleQuery, descQuery, tagsQuery); wordsQuery.descending("createdAt"); wordsQuery.limit(5); wordsQuery.find().then(function (results) { res(results); }, function (error) { res([]); }); }
這函數(shù)的作用就是通過(guò)關(guān)鍵詞words搜索,獲取滿足條件的公眾號(hào)文章,如果找不到就返回空數(shù)組。其中這里的res就是一個(gè)回調(diào)函數(shù)。在使用處:
server.route({ method: "POST", path: "/searchblog", handler: function (request, reply) { const words = request.payload.words; ModelBlog.search(words, results => { let data = []; results.forEach(function(v) { let wrap = {}; wrap.title = v.get("title"); wrap.description = v.get("desc"); wrap.picurl = v.get("picurl"); wrap.url = v.get("url"); data.push(wrap); }); reply(data); }); }, config: { validate: { payload: { words: Joi.string().required() } } } });
同樣的,在handler函數(shù)中的reply也是一個(gè)回調(diào)函數(shù),先通過(guò)ModelBlog.search函數(shù)內(nèi)嵌回調(diào)函數(shù)獲取數(shù)據(jù)庫(kù)中的文章數(shù)組,然后再對(duì)回調(diào)結(jié)果進(jìn)行處理,返回給回調(diào)函數(shù)reply,最后返回給前端使用。
通過(guò)一個(gè)簡(jiǎn)單的例子——“回調(diào)函數(shù),內(nèi)嵌回調(diào)函數(shù)”來(lái)說(shuō)明回調(diào)函數(shù)是可以無(wú)窮盡的內(nèi)嵌,結(jié)果就是各個(gè)部分之間高度耦合,流程混在一起,每個(gè)任務(wù)只能指定一個(gè)回調(diào)函數(shù)。最終造成的結(jié)果就會(huì)嵌入回調(diào)地獄,很可能就像這樣了——結(jié)尾是無(wú)止境的});:
圖片來(lái)自于:
https://tutorialzine.com/media/2017/07/callback-hell.jpg
所謂 Promise, 就是一個(gè)對(duì)象,用來(lái)傳遞異步操作的消息。它代表了某個(gè)未來(lái)才會(huì)知道結(jié)果的事件 (通常是一個(gè)異步操作),并且這個(gè)事件提供統(tǒng)一的 API,可供進(jìn)一步處理。
————來(lái)自《ES 6標(biāo)準(zhǔn)入門 (第二版)》
基于回調(diào)函數(shù)的異步處理如果統(tǒng)一參數(shù)使用規(guī)則的話,寫法也會(huì)很明了。但是,這也僅是編碼規(guī)范而已,即使采用不同的寫法也不會(huì)出錯(cuò)。
而 Promise 則是把類似的異步處理對(duì)象和處理規(guī)則進(jìn)行規(guī)范化,并按照采用統(tǒng)一的接口來(lái)編寫,而采用規(guī)定方法之外的寫法都會(huì)出錯(cuò)。
除了 Promise 對(duì)象規(guī)定的方法 (這里的 then 或 catch )以外的方法都是不可以使用的,而不會(huì)像回調(diào)函數(shù)方式那樣可以自己自由的定義回調(diào)函數(shù)的參數(shù),而必須嚴(yán)格遵守固定、統(tǒng)一的編碼方式來(lái)編寫代碼。
這樣,基于 Promise 的統(tǒng)一接口的做法,就可以形成基于接口的各種各樣的異步處理模式。所以,Promise 的功能是可以將復(fù)雜的異步處理輕松的進(jìn)行模式化,這也可以說(shuō)是使用 Promise 的理由之一。
Promise 在規(guī)范上規(guī)定 Promise 只能使用異步調(diào)用方式。
如果將上面的 demo 重新用 Promise 來(lái)寫呢:
blogs.promise_search = (words) => { const promise = new Promise(function (resolve, reject) { const titleQuery = new AV.Query(Blog) titleQuery.contains("title", words); const descQuery = new AV.Query(Blog) descQuery.contains("desc", words); const tagsQuery = new AV.Query(Blog) tagsQuery.contains("tags", words); const wordsQuery = AV.Query.or(titleQuery, descQuery, tagsQuery); wordsQuery.descending("createdAt"); wordsQuery.limit(5); wordsQuery.find().then(function (results) { resolve(results); }, function (error) { reject(error); }); }); return promise; }
Promise 構(gòu)造函數(shù)接受一個(gè)函數(shù)作為參數(shù),該函數(shù)的兩個(gè)參數(shù)分別是 resolve 和 reject。它們是兩個(gè)函數(shù),由 Javascript 引擎提供,不用自己傳入。
其中,resolve 函數(shù)的作用是,將 Promise 對(duì)象的狀態(tài)從“未完成”變成“成功” (即從 Pending 變?yōu)?Resolved),在異步操作成功時(shí)調(diào)用,并將異步操作的結(jié)果作為參數(shù)傳遞出去;
reject 函數(shù)的作用是,將 Promise 對(duì)象的狀態(tài)從“未完成”變?yōu)椤笆 ?(即從 Pending 變?yōu)?Rejected),在異步操作失敗時(shí)調(diào)用,并將異步操作報(bào)出的錯(cuò)誤作為參數(shù)傳遞出去。
Promise 實(shí)例執(zhí)行以后,可以用then方法分別指定Resolved狀態(tài)和Rejected狀態(tài)的回調(diào)函數(shù)。所以在使用處:
const words = request.payload.words; ModelBlog.promise_search(words).then(function (results) { let data = []; results.forEach(function(v) { let wrap = {}; wrap.title = v.get("title"); wrap.description = v.get("desc"); wrap.picurl = v.get("picurl"); wrap.url = v.get("url"); data.push(wrap); }); reply(data); }).catch(function (error) { reply([]); });
其中,then函數(shù)接受回調(diào)函數(shù),作為 Promise 對(duì)象的狀態(tài)變?yōu)?Resolved 時(shí)調(diào)用;而 catch 回調(diào)函數(shù)作為 Promise 對(duì)象的狀態(tài)變?yōu)?Rejected 時(shí)調(diào)用。和【異步函數(shù)】相比,簡(jiǎn)單明了很多了,至少不用再傳遞回調(diào)函數(shù)到 ModelBlog 中,可以做到代碼的分離,ModelBlog 的作用只是為了拿到數(shù)據(jù),返回 Promise 對(duì)象,具體外界怎么使用,那是別人的事情了;同樣在使用方,可以直接調(diào)用 Promise 對(duì)象,通過(guò) then 方法處理回調(diào)數(shù)據(jù)和錯(cuò)誤信息,代碼也就更容易理解了。
但寫代碼總不能到處都是 Promise 對(duì)象,既然 Promise 能解決異步調(diào)用地獄的問題,但還有沒有更好的辦法將 Promise 異步方法寫的和同步寫法那樣,畢竟很多人已經(jīng)習(xí)慣面向過(guò)程的編寫方式了?
Async/AwaitAsync/Await是一個(gè)很久就令人期待的 JavaScript 功能,它讓使用異步函數(shù)更加愉快和容易理解。它是基于 Promise 的并且和現(xiàn)存的所有基于 Promise 的 API 相兼容。
從 async 和 await 這兩個(gè)名字來(lái)的這兩個(gè)關(guān)鍵字將會(huì)幫助我們整理我們的異步代碼。
async function getBlogsAsync(words) { const titleQuery = new AV.Query(Blog) titleQuery.contains("title", words); const descQuery = new AV.Query(Blog) descQuery.contains("desc", words); const tagsQuery = new AV.Query(Blog) tagsQuery.contains("tags", words); const wordsQuery = AV.Query.or(titleQuery, descQuery, tagsQuery); wordsQuery.descending("createdAt"); wordsQuery.limit(5); let results = await wordsQuery.find(); return results; }
這下連new Promise(...)都省了,直接寫核心業(yè)務(wù)代碼。很明顯 Async/Await 版本的代碼更短并且可讀性更強(qiáng)。除了使用的語(yǔ)法,兩個(gè)函數(shù)完全相同——他們都返回 Promise 并且都從數(shù)據(jù)庫(kù)得到 Blogs 數(shù)據(jù)返回。在使用時(shí),還是和之前一樣,直接調(diào)用getBlogsAsync方法:
getBlogsAsync(words).then(function (results) { let data = []; results.forEach(function(v) { let wrap = {}; wrap.title = v.get("title"); wrap.description = v.get("desc"); wrap.picurl = v.get("picurl"); wrap.url = v.get("url"); data.push(wrap); }); reply(data); }).catch(function (error) { reply([]); });總結(jié)
隨著 Async/Await ,JavaScript語(yǔ)言在代碼可讀性和易用性上向前邁進(jìn)了一大步。而且寫異步代碼,就跟常規(guī)的寫面向過(guò)程的同步代碼一樣,簡(jiǎn)單直接明了。
最后分享幾個(gè)相關(guān)資料,值得一看,還有更多深入的內(nèi)容需要繼續(xù)挖掘:
Javascript異步編程的4種方法。http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html
《ES 6標(biāo)準(zhǔn)入門 (第二版)》,作者:阮一峰
JavaScript Async/Await Explained in 10 Minutes. https://tutorialzine.com/2017/07/javascript-async-await-explained
八段代碼徹底掌握 Promise. https://juejin.im/post/597724c26fb9a06bb75260e8
JavaScript Promise迷你書(中文版) http://liubin.org/promises-book/#promises-overview
理解 async/await. https://juejin.im/post/596e142d5188254b532ce2da
聽說(shuō)最美的人和最帥的人,都會(huì)給作者打賞,以資鼓勵(lì)
coding01 期待您關(guān)注
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/84604.html
摘要:想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳博客一年百來(lái)篇優(yōu)質(zhì)文章等著你引入的在的異步編程中是一個(gè)極好的改進(jìn)??赡軙?huì)產(chǎn)生誤導(dǎo)一些文章將與進(jìn)行了比較,并聲稱它是下一代異步編程風(fēng)格,對(duì)此作者深表異議。結(jié)論引入的關(guān)鍵字無(wú)疑是對(duì)異步編程的改進(jìn)。 showImg(https://segmentfault.com/img/bVbjFP0?w=800&h=450); 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇...
摘要:事件循環(huán)從回調(diào)隊(duì)列中獲取并將其推入調(diào)用堆棧。執(zhí)行從調(diào)用堆棧中移除從調(diào)用堆棧中移除快速回顧值得注意的是,指定了事件循環(huán)應(yīng)該如何工作,這意味著在技術(shù)上它屬于引擎的職責(zé)范圍,不再僅僅扮演宿主環(huán)境的角色。 此篇是 JavaScript是如何工作的第四篇,其它三篇可以看這里: JavaScript是如何工作的:引擎,運(yùn)行時(shí)和調(diào)用堆棧的概述! JavaScript是如何工作的:深入V8引擎&編寫...
摘要:事件循環(huán)從回調(diào)隊(duì)列中獲取并將其推送到調(diào)用堆棧。如何工作請(qǐng)注意,不會(huì)自動(dòng)將您的回調(diào)函數(shù)放到事件循環(huán)隊(duì)列中。它設(shè)置了一個(gè)計(jì)時(shí)器,當(dāng)計(jì)時(shí)器到期時(shí),環(huán)境將您的回調(diào)函數(shù)放入事件循環(huán)中,以便將來(lái)的某個(gè)事件會(huì)將其選中并執(zhí)行它。 我們將通過(guò)回顧第一篇文章中單線程編程的缺點(diǎn),然后在討論如何克服它們來(lái)構(gòu)建令人驚嘆的JavaScript UI。在文章結(jié)尾處,我們將分享5個(gè)關(guān)于如何使用async / awai...
摘要:函數(shù)會(huì)在之后的某個(gè)時(shí)刻觸發(fā)事件定時(shí)器。事件循環(huán)中的這樣一次遍歷被稱為一個(gè)。執(zhí)行完畢并出棧。當(dāng)定時(shí)器過(guò)期,宿主環(huán)境會(huì)把回調(diào)函數(shù)添加至事件循環(huán)隊(duì)列中,然后,在未來(lái)的某個(gè)取出并執(zhí)行該事件。 原文請(qǐng)查閱這里,略有改動(dòng)。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第四章。 現(xiàn)在,我們將會(huì)通過(guò)回顧單線程環(huán)境下編程的弊端及如何克服這些困難以創(chuàng)建令人驚嘆...
摘要:一方面,這里替代的是異步代碼的編寫方式,并非完全拋棄大家心愛的,地球人都知道是基于的,不用太傷心另一方面,是基于回調(diào)函數(shù)實(shí)現(xiàn)的,那也沒有替代回調(diào)函數(shù)咯重構(gòu)代碼之后,我仍然用到了庫(kù)。 摘要: 夸張點(diǎn)說(shuō),技術(shù)的發(fā)展與歷史一樣,順之者昌,逆之者亡。JS開發(fā)者們,趕緊擁抱Async/Await吧! GitHub倉(cāng)庫(kù): Fundebug/promise-asyncawait 早在半年多之前,...
閱讀 2390·2021-11-24 09:39
閱讀 2596·2021-11-22 15:24
閱讀 3042·2021-09-02 09:48
閱讀 3096·2021-07-26 22:01
閱讀 1498·2019-08-30 11:09
閱讀 1737·2019-08-29 18:47
閱讀 666·2019-08-29 15:40
閱讀 2187·2019-08-29 15:22