摘要:所謂異步編程中的異步是相對(duì)于同步的概念的。是一系列異步編程規(guī)范的統(tǒng)稱。如果中的回調(diào)函數(shù)返回一個(gè)值,那么返回的將會(huì)成為接受狀態(tài),并且將返回的值作為接受狀態(tài)的回調(diào)函數(shù)的參數(shù)值。參考介紹基礎(chǔ)篇深入理解與異步編程。
es6 promise與異步編程
對(duì)于一些還不具備大量編程經(jīng)驗(yàn)的朋友來(lái)說(shuō),promise可能是es6比較難以掌握的點(diǎn)。首先是很多名詞,比如Promises,es6 Promise, 回調(diào)函數(shù)(callback),Promise/A+,異步編程等。下面就首先介紹下這些名詞的含義和區(qū)別。
所謂異步編程中的異步是相對(duì)于同步的概念的。js是單線程的語(yǔ)言,同一時(shí)間只能做一件事,為了指定一些稍后要執(zhí)行的代碼,我們需要異步。在客戶端,主要的異步方式有事件,setTimeout,Ajax等。Node的發(fā)展大大擴(kuò)展了js語(yǔ)言的邊界,我們知道,Node使用非阻塞IO模型,它使用回調(diào)函數(shù)模式來(lái)實(shí)現(xiàn)異步編程。比如:
readFile("example.txt", function(err, contents){ if(err){ throw err; } console.log(contents); }); console.log("Hi!");
上面代碼中readFile的第二個(gè)參數(shù)就是回調(diào)函數(shù)。它會(huì)在讀取完example.txt后被添加到執(zhí)行隊(duì)列中。上面代碼的執(zhí)行順序是--執(zhí)行readFile函數(shù),在遇到讀取文件時(shí)暫停,打印"Hi",讀取文件結(jié)束后將回調(diào)添加到作業(yè)隊(duì)列中,執(zhí)行回調(diào)函數(shù),打印contents。
本來(lái)呢,使用回調(diào)函數(shù)是能夠完成異步編程的。但是隨著代碼的邏輯越復(fù)雜,這種異步編程方式越來(lái)越難以閱讀和追蹤程序錯(cuò)誤,所以發(fā)展出了Promises規(guī)范來(lái)完成異步編程。
Promises是一系列異步編程規(guī)范的統(tǒng)稱。我們需要了解的是其中的Promise/A+規(guī)范。es6通過(guò)Promise這個(gè)內(nèi)建對(duì)象實(shí)現(xiàn)了該規(guī)范。所以我們可以使用es6中的Promise對(duì)象來(lái)進(jìn)行異步編程。
下面將對(duì)es6中的Promise對(duì)象進(jìn)行介紹。至于jQuery中延遲對(duì)象$.deferred(),根據(jù)規(guī)范自己實(shí)現(xiàn)promise和ES7的Async/Await異步方式等更多內(nèi)容,后面會(huì)專門寫一篇文章進(jìn)行介紹。
語(yǔ)法 Promise的3種狀態(tài)一個(gè)promise實(shí)例有3種狀態(tài),分別是:
pending -- 掛起,表示Promise結(jié)果還未知。
fulfilled -- 已完成, 表示Promise成功完成。
rejected -- 已拒絕,表示Promise未成功結(jié)束。
promise處于這3種狀態(tài)中的一種,并且可以由pending狀態(tài)變?yōu)閒ulfilled狀態(tài),或由pending變?yōu)閞ejected狀態(tài)。反之則不行。
為了便于理解,下面將通過(guò)一個(gè)生活化的例子,來(lái)解釋什么是Promise?
Promise是允諾的意思。它就是一個(gè)關(guān)于未發(fā)生的事情的承諾。比如:
你訂了一份燒烤,店家說(shuō)半個(gè)小時(shí)內(nèi)送到,這就是一個(gè)Promise?,F(xiàn)在,這個(gè)Promise還沒(méi)有發(fā)生,所以可能半個(gè)小時(shí)內(nèi)配送成功或者失敗。對(duì)此,你預(yù)備了兩種處理方式:成功 -- 美滋滋的吃燒烤,失敗 -- 去樓下店里吃。
在半個(gè)小時(shí)內(nèi),這個(gè)Promise處于pending狀態(tài),你正常上網(wǎng),擼代碼。一段時(shí)間后,這個(gè)promise就有了結(jié)果。是成功(fulfilled)或者失敗(rejected)。根據(jù)這個(gè)結(jié)果,你之前的兩種處理方式就會(huì)相應(yīng)執(zhí)行。這就是promise。
對(duì)應(yīng)的代碼如下:
let promise = new Promise(function(resolve, reject){ //等待店家送來(lái)中... let result = "配送成功"? true : false; if(result){ resolve(value); }else{ reject(reason); } }); promise.then(function(value){ //美滋滋吃燒烤... //value為上面resolve()中傳遞的值, 比如共100塊錢。 }, function(reason){ //叫上隔壁老王去樓下吃... //reason為上面reject()的傳遞的原因,比如烤糊了... });
上面代碼就是通過(guò)promise異步編程的代碼。這里要注意的是Promise構(gòu)造函數(shù)接收一個(gè)函數(shù)作為參數(shù),函數(shù)內(nèi)部是異步的邏輯。這個(gè)函數(shù)接收兩個(gè)參數(shù):resolve和reject。resolve()可以把promise推向fulfilled狀態(tài),reject()可以把promise推向rejected狀態(tài)。
promise有個(gè)then方法,用于處理promise成功或失敗后的邏輯。then有兩個(gè)參數(shù):
參數(shù)1為promise成功時(shí)執(zhí)行的函數(shù),該函數(shù)的參數(shù)value對(duì)應(yīng)于上面resolve(value)中的value值;
參數(shù)2為promise失敗時(shí)執(zhí)行的函數(shù),該函數(shù)的參數(shù)reason對(duì)應(yīng)于reject(reason)中的reason值,表示失敗的原因。
一旦promise有了結(jié)果(成功或失敗),就會(huì)執(zhí)行對(duì)應(yīng)then中的函數(shù)。
Ajax是客戶端最常用的異步編程場(chǎng)景,下面一個(gè)例子演示了使用Promise進(jìn)行Ajax操作的代碼。
function getData(method, url){ let promise = new Promise(function(resolve, reject){ let xmlHttp = new XMLHttpRequest(); xmlHttp.open(method, url); xmlHttp.send(); xmlHttp.onload = function () { if (this.status == 200 ) { resolve(this.response); } else { reject(this.statusText); } }; xmlHttp.onerror = function () { reject(this.statusText); }; }) return promise; } getData("get","www.xxx.com").then(successFun, failFun); function successFun(value){ //Ajax成功處理函數(shù)... } function failFun(reason){ //Ajax失敗處理函數(shù)... }創(chuàng)建一個(gè)已決的Promise
前面的例子promise創(chuàng)建時(shí),promise都處于pending狀態(tài),根據(jù)異步操作的結(jié)果將promise推向成功或失敗狀態(tài)。
Promise類型有兩個(gè)靜態(tài)方法Promise.resolve(value),Promise.reject(reason)可以分別創(chuàng)建已經(jīng)是fulfilled和已經(jīng)是rejected狀態(tài)的promise。
比如:
let promise = Promise.resolve(44); promise.then(function(value){ console.log("fulfilled", value); })
上面代碼promise在被創(chuàng)建出來(lái)時(shí),已經(jīng)是fulfilled狀態(tài),接下來(lái)會(huì)直接將then中的回調(diào)函數(shù)加入到作業(yè)隊(duì)列中,等待作業(yè)隊(duì)列中前面的任務(wù)完成后執(zhí)行該函數(shù)。
這里傳入Promise.resolve(value)和Promise.reject(reason)中的參數(shù)和之前Promise構(gòu)造是對(duì)應(yīng)的參數(shù)是一樣的。
Promise.prototype.then()和Promise.prototype.catch()上面已經(jīng)演示過(guò)promise實(shí)例上then方法的用法,每一個(gè)promise實(shí)例還具有catch方法。
catch()方法只處理reject的情況,他的行為與調(diào)用Promise.prototype.then(undefined, onRejected)相同。比如:
let p = new Promise(function(resolve, reject){ //... reject(new Error("something wrong!")) }) p.catch(function(reason){ //拒絕 })
上面catch方法中的回調(diào)在promise被reject時(shí)調(diào)用。
then()和catch()的返回值每次對(duì)then()或catch()的調(diào)用都會(huì)返回另一個(gè)promise,這也是很多代碼可以寫成類似鏈?zhǔn)秸{(diào)用的原因。比如:
let p1 = new Promise(function(resolve, reject){ resolve(42); }); let p2 = p1.then(function(value){ console.log(value); }) p2.then(function(){ console.log("Finished"); }, function(){ console.log("something wrong!"); }); p1 == p2 // false,注意:p1.then()會(huì)返回一個(gè)新的promise,所以p1與p2并不相等 //可以寫成鏈?zhǔn)秸{(diào)用的形式,比如 p1.then(function(value){ console.log(value) }).then(function(){ console.log("do something"); }).then(function(){ console.log("Finished"); })
在上面代碼中,p1.then()返回了一個(gè)promise為p2, 那么p2的狀態(tài)和p1之間有什么關(guān)系呢?
更具體一點(diǎn)說(shuō),當(dāng)p1變?yōu)閒ulfilled時(shí),p1.then()返回的p2是什么狀態(tài)呢?二者有什么聯(lián)系呢?
p2的行為與p1.then()中回調(diào)函數(shù)的返回值有關(guān):
如果then中的回調(diào)函數(shù)拋出一個(gè)錯(cuò)誤,或者回調(diào)函數(shù)中調(diào)用reject(reason),那么then返回的Promise將會(huì)成為拒絕狀態(tài),并且將拋出的錯(cuò)誤作為拒絕狀態(tài)的回調(diào)函數(shù)的參數(shù)值。
如果then中的回調(diào)函數(shù)返回一個(gè)值,那么then返回的Promise將會(huì)成為接受狀態(tài),并且將返回的值作為接受狀態(tài)的回調(diào)函數(shù)的參數(shù)值。
如果then中的回調(diào)函數(shù)返回一個(gè)已經(jīng)是接受狀態(tài)的Promise,那么then返回的Promise也會(huì)成為接受狀態(tài),并且將那個(gè)Promise的接受狀態(tài)的回調(diào)函數(shù)的參數(shù)值作為該被返回的Promise的接受狀態(tài)回調(diào)函數(shù)的參數(shù)值。
如果then中的回調(diào)函數(shù)返回一個(gè)已經(jīng)是拒絕狀態(tài)的Promise,那么then返回的Promise也會(huì)成為拒絕狀態(tài),并且將那個(gè)Promise的拒絕狀態(tài)的回調(diào)函數(shù)的參數(shù)值作為該被返回的Promise的拒絕狀態(tài)回調(diào)函數(shù)的參數(shù)值。
如果then中的回調(diào)函數(shù)返回一個(gè)未定狀態(tài)(pending)的Promise,那么then返回Promise的狀態(tài)也是未定的,并且它的終態(tài)與那個(gè)Promise的終態(tài)相同;同時(shí),它變?yōu)榻K態(tài)時(shí)調(diào)用的回調(diào)函數(shù)參數(shù)與那個(gè)Promise變?yōu)榻K態(tài)時(shí)的回調(diào)函數(shù)的參數(shù)是相同的。
如果then中的回調(diào)函數(shù)無(wú)顯式的返回值,并且也沒(méi)有調(diào)用reject(),那么返回的Promise為接收狀態(tài)。
Promise.all()處理多個(gè)promisePromise內(nèi)建對(duì)象上的靜態(tài)方法Promise.all()用于處理多個(gè)promise的情況。
Promise.all([promise1, promise2,...])返回一個(gè)promise的實(shí)例,接收一個(gè)promise組成的數(shù)組為參數(shù)。只有當(dāng)數(shù)組內(nèi)的promise都成功時(shí),才會(huì)調(diào)用對(duì)應(yīng)的then中的成功處理函數(shù),只要有一個(gè)不成功,那么調(diào)用對(duì)應(yīng)的拒絕處理函數(shù)。
依然使用前面那么訂燒烤的例子,你不僅訂了燒烤,還在另一家訂了啤酒。打算等到燒烤和啤酒都配送成功后一起吃,美滋滋~~。比如:
Promise.all([訂燒烤,訂啤酒]).then(function(value){ //吃燒烤,喝啤酒... }, function(reason){ //拒絕的原因,烤糊了或者啤酒賣完了... })
這里要注意的一點(diǎn)是,對(duì)于數(shù)組中的promise,只要有任一個(gè)promise為拒絕,那么就會(huì)立即執(zhí)行then中的拒絕處理函數(shù),并不會(huì)等待其他promise的結(jié)果。只有當(dāng)所有promise的結(jié)果都成功時(shí),才執(zhí)行then中的成功處理函數(shù)。比如:
var p1 = new Promise(function(resolve, reject){ setTimeout(function(){ console.log("A"); resolve(); }, 1000) }); var p2 = Promise.reject(new Error("error")); var p3 = new Promise(function(resolve, reject){ setTimeout(function(){ console.log("B"); resolve(); }, 0) }); Promise.all([p1,p2,p3]).then(function(value){ console.log("success!"); }, function(reason){ console.log("failed"); }) //結(jié)果為failed B A
由于p2為已拒絕狀態(tài)的promise,所以Promise.all()立即變?yōu)榫芙^狀態(tài),打印failed,p1和p2會(huì)繼續(xù)執(zhí)行,但對(duì)于Promise.all()的結(jié)果沒(méi)有影響。
Promise.race()處理多個(gè)promisePromise內(nèi)建對(duì)象上的靜態(tài)方法Promise.race()同樣用于處理多個(gè)promise的情況。同樣返回一個(gè)Promise,同樣接收一個(gè)promise數(shù)組作為參數(shù)。
與all不同的地方在于,數(shù)組中的promise就像在賽跑一樣(race),并且只關(guān)心第一名的情況,只要有其中一個(gè)promise有了結(jié)果,Promise.race()的狀態(tài)就會(huì)立即與該promise相同。
數(shù)組中其他promise繼續(xù)執(zhí)行,但對(duì)于Promise.race()的結(jié)果沒(méi)有影響。
我們構(gòu)造promise實(shí)例的代碼是立即執(zhí)行的,而then方法中的回調(diào)函數(shù)是異步調(diào)用的,在promise的狀態(tài)變?yōu)槌晒蚓芙^時(shí),才會(huì)把相應(yīng)的處理函數(shù)添加到promise工作隊(duì)列中。并且該函數(shù)會(huì)先于setTimeout執(zhí)行。例如:
var promise = new Promise(function(resolve, reject){ console.log("A"); resolve("C"); }) console.log("B"); setTimeout(function(){ console.log("D"); },0) promise.then(function(value){ console.log(value) }); //打印A, B, C, D
如果then方法中傳入的參數(shù)被忽略,或者是非函數(shù),比如:
p.then(function(value){ //... }) //或者 p.then(undefined, function(reason){ //... })
那么,相應(yīng)的回調(diào)處理函數(shù)被忽略,then方法返回的promise會(huì)保留上一個(gè)promise的狀態(tài)和參數(shù)。最典型的例子:
var p = new Promise(function(resolve, reject){ reject(new Error("error")); }) p.then(function(value){ //... }).then(function(value){ //... }).then(undefined, function(reason){ console.log(reason); }) //打印"error"
p的前兩次then調(diào)用的拒絕處理函數(shù)被忽略,然后reject狀態(tài)和錯(cuò)誤信息就一直往后傳遞,直到被最后一次then調(diào)用捕獲。
最佳實(shí)踐關(guān)于Promise的知識(shí)點(diǎn)很多,但是最常用的場(chǎng)景就是Ajax。比如:
function getData(method, url){ var promise = new Promise(function(resolve, reject){ //Ajax獲取數(shù)據(jù)的代碼... if(success){ resolve(response) }else{ reject(statusText) } }) return promise; } getData("get","www.xxx.com").then(Fun1).then(Fun2).then(Fun3).catch(function(reason){ //錯(cuò)誤處理邏輯... });
更多關(guān)于es6的內(nèi)容,可以關(guān)注右側(cè)我的專欄--學(xué)習(xí)ES6。
參考:
MDN Javascript Promise.
Promise介紹-基礎(chǔ)篇.
《深入理解ES6》-- Promise與異步編程。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/93249.html
摘要:的翻譯文檔由的維護(hù)很多人說(shuō),阮老師已經(jīng)有一本關(guān)于的書了入門,覺(jué)得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開(kāi)發(fā)過(guò)程中,顯得越來(lái)越重要。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:和和都有和,但是略有不同。實(shí)際上返回的是一個(gè)對(duì)象。和添加的回調(diào),添加的回調(diào)。所以在調(diào)用成功的情況下執(zhí)行添加的回調(diào),調(diào)用失敗時(shí)執(zhí)行添加的回調(diào)。,產(chǎn)生對(duì)象并,產(chǎn)生對(duì)象并,然后繼續(xù)處理,的語(yǔ)法糖,和的差不多但不同。 Deferred 和 Promise ES6 和 jQuery 都有 Deffered 和 Promise,但是略有不同。不過(guò)它們的作用可以簡(jiǎn)單的用兩句話來(lái)描述 Deffere...
摘要:從最開(kāi)始的到封裝后的都在試圖解決異步編程過(guò)程中的問(wèn)題。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。寫一個(gè)符合規(guī)范并可配合使用的寫一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來(lái)處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問(wèn)題描述 在開(kāi)發(fā)過(guò)程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過(guò)http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過(guò)...
摘要:如果有錯(cuò)誤,則到的第二個(gè)回調(diào)函數(shù)中,對(duì)錯(cuò)誤進(jìn)行處理。假設(shè)第一個(gè)的第一個(gè)回調(diào)沒(méi)有返回一個(gè)對(duì)象,那么第二個(gè)的調(diào)用者還是原來(lái)的對(duì)象,只不過(guò)其的值變成了第一個(gè)中第一個(gè)回調(diào)函數(shù)的返回值。 ES6標(biāo)準(zhǔn)出爐之前,一個(gè)幽靈,回調(diào)的幽靈,游蕩在JavaScript世界。 正所謂: 世界本沒(méi)有回調(diào),寫的人多了,也就有了})})})})})。 Promise的興起,是因?yàn)楫惒椒椒ㄕ{(diào)用中,往往會(huì)出現(xiàn)回調(diào)函數(shù)一...
摘要:就算改變已經(jīng)發(fā)生了,即使再對(duì)對(duì)象添加回調(diào)函數(shù),也會(huì)立即得到這個(gè)結(jié)果。方法接收個(gè)參數(shù),第一個(gè)參數(shù)是狀態(tài)的回調(diào)函數(shù),第二個(gè)參數(shù)可選是狀態(tài)的回調(diào)函數(shù)。簡(jiǎn)單來(lái)講,就是能把原來(lái)的回調(diào)寫法分離出來(lái),在異步操作執(zhí)行完后,用鏈?zhǔn)秸{(diào)用的方式執(zhí)行回調(diào)函數(shù)。 在ECMAScript 6標(biāo)準(zhǔn)中,Promise被正式列為規(guī)范,Promise,字面意思就是許諾,承諾,嘿,聽(tīng)著是不是很浪漫的說(shuō)?我們來(lái)探究一下這個(gè)浪...
閱讀 2985·2021-09-22 15:18
閱讀 3401·2019-08-30 15:54
閱讀 3282·2019-08-30 15:53
閱讀 602·2019-08-30 14:12
閱讀 821·2019-08-29 17:01
閱讀 2209·2019-08-29 14:04
閱讀 1401·2019-08-29 13:09
閱讀 873·2019-08-26 17:40