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