摘要:今天對于處理異步調(diào)用已經(jīng)有了很多成熟的方案,在我看來這些方案都無外乎在解決一個(gè)問題如何能看似順序地傳遞異步調(diào)用的結(jié)果,本文要說的就是原生提供的一個(gè)解決方案。在對進(jìn)行敘述之前,依舊引用阮大的入門一書中的章節(jié)便于大家更嚴(yán)謹(jǐn)和全面的學(xué)習(xí)和參考。
異步回調(diào)的泥潭
異步回調(diào)是最直接的異步結(jié)果處理模式,將一個(gè)回調(diào)函數(shù)callback扔進(jìn)異步處理函數(shù)中,當(dāng)異步處理獲得結(jié)果之后再調(diào)用這個(gè)回調(diào)函數(shù)就可以繼續(xù)之后的處理,但是如果這個(gè)callback又是一個(gè)異步調(diào)用呢?眾所周知的,在JavaScript中異步回調(diào)的層層嵌套幾乎是反人性的,代碼編寫、修改和閱讀對我等有代碼潔癖的人而言是一種煎熬,這便是異步回調(diào)泥潭了。
今天對于處理異步調(diào)用已經(jīng)有了很多成熟的方案,在我看來這些方案都無外乎在解決一個(gè)問題:“如何能看似順序地傳遞異步調(diào)用的結(jié)果?”,本文要說的Promise就是ES6原生提供的一個(gè)解決方案。
Promise在對Promise進(jìn)行敘述之前,依舊引用阮大的《ECMAScript 6入門》一書中的Promise章節(jié)便于大家更嚴(yán)謹(jǐn)和全面的學(xué)習(xí)和參考。
承諾,即對未來的許諾,如果諾言實(shí)現(xiàn),然后(then)就如何如何……Promise極其生動的講述了一個(gè)言出必行的故事。
new Promise(function(resolve, reject){ //開始實(shí)現(xiàn)承諾 .... .... if(承諾兌現(xiàn)時(shí)) { resolve(dollars); //兌現(xiàn)承諾的結(jié)果是得到"一大筆美金" } else { reject("絕交"); //沒兌現(xiàn)承諾就絕交 } }).then(function(dollars){ //然后有錢了,買房買車娶妻生子 let d1 = buyHouse(dollars); //把每次消費(fèi)剩余的錢傳給下一個(gè)函數(shù) let d2 = buyCar(d1); let d3 = marry(d2); makeBaby(d3); }).catch(function(result){//然后如果絕交了,還是繼續(xù)吃土 //繼續(xù)吃土 }); console.log("故事開始....");
看過上面的這個(gè)俗不可耐的故事之后需要理解幾件事情:
言出必行:一個(gè)Promise構(gòu)造出來之后,構(gòu)造時(shí)傳入的異步函數(shù)就立即執(zhí)行;*
注:因大凡使用promise都是在異步調(diào)用場景,下文所說的異步函數(shù)都是指構(gòu)造promise時(shí)傳入的函數(shù)*
Promise實(shí)例內(nèi)部維護(hù)了一個(gè)狀態(tài)機(jī),狀態(tài)變化只可能是pending到resolved或者pending到rejected;
執(zhí)行resolve:pending變化到resolved
執(zhí)行reject:pending變化到rejected
拋出錯(cuò)誤:pending變化到rejected
then的第一個(gè)回調(diào)函數(shù)只會在發(fā)生了resolve之后執(zhí)行,本質(zhì)上是在Promise到達(dá)resolved狀態(tài)執(zhí)行;
then的第二個(gè)回調(diào)函數(shù)或者catch的回調(diào)函數(shù)會在發(fā)生reject之后或者異步函數(shù)執(zhí)行拋出錯(cuò)誤時(shí)執(zhí)行,本質(zhì)上是在promise到達(dá)rejected狀態(tài)時(shí)執(zhí)行;
異步函數(shù)執(zhí)行得到結(jié)果可以通過resolve或者reject將結(jié)果傳出;
調(diào)用resolve傳入的值會作為then第一個(gè)回調(diào)函數(shù)的入?yún)?/p>
調(diào)用reject傳入的值作為then第二個(gè)回調(diào)函數(shù)或者catch的回調(diào)函數(shù)的入?yún)?/p>
如果異步函數(shù)拋出了異常,異常會作為then第二個(gè)回調(diào)函數(shù)或者catch的回調(diào)函數(shù)的入?yún)?/p>
"故事開始...."會先輸出,而不是等到then的回調(diào)函數(shù)執(zhí)行完畢才輸出,說明傳入then的回調(diào)函數(shù)是異步執(zhí)行,同理catch也是一樣;
異步函數(shù)調(diào)用鏈then和catch都是Promise的實(shí)例方法,都返回一個(gè)新的Promise,因此可以輕而易舉地實(shí)現(xiàn)鏈?zhǔn)骄幊?,比如上面的例子中“把每次消費(fèi)剩余的錢”傳給下一個(gè)函數(shù)可以改寫成這樣:
....//前面省略 .then(function(dollars){ return buyHouse(dollars); }).then(function(d1){ return buyCar(d1); }).then(function(d2){ return marry(d2); }).then(function(d3){ return makeBaby(d3); }).catch(function(result){ //繼續(xù)吃土 });
看到這里你可能認(rèn)為前一個(gè)then回調(diào)函數(shù)的返回值是后一個(gè)then的回調(diào)函數(shù)的入?yún)ⅲ@是不準(zhǔn)確的,因?yàn)楫?dāng)then回調(diào)函數(shù)返回的是個(gè)Promise對象時(shí),這個(gè)Promise對象到終態(tài)時(shí)后一個(gè)then才會執(zhí)行,并且該Promise對象執(zhí)行resolve時(shí)的入?yún)⒉攀呛笠粋€(gè)then的回調(diào)函數(shù)入?yún)ⅲ?/p>
此時(shí)有必要對Promise的一個(gè)類方法resolve做以下說明,它的特性兩句話:
如果傳入的是個(gè)Promise對象,則直接返回這個(gè)Promise;
如果是其他任何一個(gè)值(包括Error對象和undefined)則直接轉(zhuǎn)換為一個(gè)resolved狀態(tài)的Promise對象;
比如說下面的代碼:
//以下的p1和p2邏輯上等同 let p1 = Promise.resolve(1); let p2 = new Promise(function(resolve, reject) { resolve(1); }); //以下的p3和p4等同 let p3 = new Promise(function(r, j) {}); let p4 = Promise.resolve(p3); console.log(p3 == p4); //true console.log(p3 === p4); //true //以下三者邏輯上等同 Promise.resolve().then(function(dollars) { return 1 + 1; }).then(function(v) { console.log(v); }); Promise.resolve().then(function(dollars) { return new Promise(function(r, j) { r(1 + 1) }); }).then(function(v) { console.log(v); }); Promise.resolve().then(function(dollars) { return Promise.resolve(1 + 1); }).then(function(v) { console.log(v); });
我們可以利用Promise異步執(zhí)行結(jié)果傳出的機(jī)制和then的鏈?zhǔn)秸{(diào)用,將層層嵌套的函數(shù)調(diào)用變?yōu)橥ㄟ^then順序連接的鏈?zhǔn)秸{(diào)用
從寫法和形式上看是不是人性很多呢?
通過Promise實(shí)現(xiàn)的鏈?zhǔn)疆惒胶瘮?shù)調(diào)用,以斐波那契數(shù)列舉例如下:
//一個(gè)異步的斐波那契計(jì)算 function fibonacci(v) { return new Promise(function(resolve, reject) { //每一個(gè)異步調(diào)用都返回了一個(gè)Promise setTimeout(function() { console.log(`${v.a}`); [v.a, v.b] = [v.b, v.a + v.b]; resolve(v); }, 500); }); } //以下兩者邏輯等同,每個(gè)then都等待上一個(gè)promise的結(jié)果形成一條鏈。 // fibonacci({ a: 0, b: 1 }) // .then(fibonacci) // .then(fibonacci) // .then(fibonacci) // .then(fibonacci) // .then(fibonacci) // .then(fibonacci); Promise.resolve() .then(() => fibonacci({ a: 0, b: 1 })) .then(fibonacci) .then(fibonacci) .then(fibonacci) .then(fibonacci) .then(fibonacci) .then(fibonacci);
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/86780.html
摘要:本文參考了以下文章之前的文章新特性印象之一新語法面對對象關(guān)鍵字看上面例子就能明白。定義類的,配合創(chuàng)建新對象。繼承非構(gòu)造器對象的原型是。錯(cuò)誤檢查繼承的目標(biāo)一定要是個(gè)對象或者。的構(gòu)造器是可改寫,但不可枚舉。引入了一個(gè)標(biāo)簽,負(fù)責(zé)載入模塊。 本文參考了以下文章/PPT: Use ECMAScript 6 today Ecmascript 6 Whats next for Javascrip...
摘要:的翻譯文檔由的維護(hù)很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個(gè)符合規(guī)范并可配合使用的寫一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過...
摘要:回調(diào)函數(shù)這是異步編程最基本的方法。對象對象是工作組提出的一種規(guī)范,目的是為異步編程提供統(tǒng)一接口。誕生后,出現(xiàn)了函數(shù),它將異步編程帶入了一個(gè)全新的階段。 更多詳情點(diǎn)擊http://blog.zhangbing.club/Ja... Javascript 語言的執(zhí)行環(huán)境是單線程的,如果沒有異步編程,根本沒法用,非卡死不可。 為了解決這個(gè)問題,Javascript語言將任務(wù)的執(zhí)行模式分成兩種...
摘要:所謂異步編程中的異步是相對于同步的概念的。是一系列異步編程規(guī)范的統(tǒng)稱。如果中的回調(diào)函數(shù)返回一個(gè)值,那么返回的將會成為接受狀態(tài),并且將返回的值作為接受狀態(tài)的回調(diào)函數(shù)的參數(shù)值。參考介紹基礎(chǔ)篇深入理解與異步編程。 es6 promise與異步編程 對于一些還不具備大量編程經(jīng)驗(yàn)的朋友來說,promise可能是es6比較難以掌握的點(diǎn)。首先是很多名詞,比如Promises,es6 Promise,...
閱讀 3161·2021-11-22 12:01
閱讀 3781·2021-08-30 09:46
閱讀 796·2019-08-30 13:48
閱讀 3230·2019-08-29 16:43
閱讀 1674·2019-08-29 16:33
閱讀 1864·2019-08-29 13:44
閱讀 1428·2019-08-26 13:45
閱讀 2242·2019-08-26 11:44