摘要:就算改變已經(jīng)發(fā)生了,你再對(duì)對(duì)象添加回調(diào)函數(shù),也會(huì)立即得到這個(gè)結(jié)果。有了對(duì)象,就可以將異步操作以同步操作的流程表達(dá)出來,避免了層層嵌套的回調(diào)函數(shù)。但是在這里,會(huì)得到這樣的結(jié)果關(guān)于是用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)。
看了很多關(guān)于promise的文章,此篇文章做以總結(jié)。
由于Javascript是一種單線程的語言,所有的代碼必須按照所謂的“自上而下”的順序來執(zhí)行。本特性帶來的問題就是,一些將來的、未知的操作,必須異步實(shí)現(xiàn)。promise就是一個(gè)比較常見的異步解決方案。
Promise有以下兩個(gè)特點(diǎn):
(1)狀態(tài)不受外界影響。Promise對(duì)象代表一個(gè)異步操作,有三種狀態(tài):Pending(進(jìn)行中)、Resolved(已完成,又稱 Fulfilled)和Rejected(已失?。V挥挟惒讲僮鞯慕Y(jié)果,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無法改變這個(gè)狀態(tài)。這也是Promise這個(gè)名字的由來,它的英語意思就是“承諾”,表示其他手段無法改變。
(2)一旦狀態(tài)改變,就不會(huì)再變,任何時(shí)候都可以得到這個(gè)結(jié)果。Promise對(duì)象的狀態(tài)改變,只有兩種可能:從Pending變?yōu)镽esolved和從Pending變?yōu)镽ejected。只要這兩種情況發(fā)生,狀態(tài)就凝固了,不會(huì)再變了,會(huì)一直保持這個(gè)結(jié)果。就算改變已經(jīng)發(fā)生了,你再對(duì)Promise對(duì)象添加回調(diào)函數(shù),也會(huì)立即得到這個(gè)結(jié)果。這與事件(Event)完全不同,事件的特點(diǎn)是,如果你錯(cuò)過了它,再去監(jiān)聽,是得不到結(jié)果的。
有了Promise對(duì)象,就可以將異步操作以同步操作的流程表達(dá)出來,避免了層層嵌套的回調(diào)函數(shù)。此外,Promise對(duì)象提供統(tǒng)一的接口,使得控制異步操作更加容易。
首先,看一個(gè)例子,new一個(gè)promise
var p = new Promise(function(resolve, reject){ //做一些異步操作 setTimeout(function(){ console.log("執(zhí)行完成"); resolve("成功的數(shù)據(jù)"); }, 2000); });
Promise的構(gòu)造函數(shù)接收一個(gè)參數(shù),是函數(shù),并且傳入兩個(gè)參數(shù):resolve,reject,分別表示異步操作執(zhí)行成功后的回調(diào)函數(shù)和異步操作執(zhí)行失敗后的回調(diào)函數(shù),按照標(biāo)準(zhǔn)來講,resolve是將Promise的狀態(tài)置為fullfiled,reject是將Promise的狀態(tài)置為rejected。
在上面的代碼中,我們執(zhí)行了一個(gè)異步操作,也就是setTimeout,2秒后,輸出“執(zhí)行完成”,并且調(diào)用resolve方法。
運(yùn)行代碼,會(huì)在2秒后輸出“執(zhí)行完成”。注意!我只是new了一個(gè)對(duì)象,并沒有調(diào)用它,我們傳進(jìn)去的函數(shù)就已經(jīng)執(zhí)行了,這是需要注意的一個(gè)細(xì)節(jié)。所以我們用Promise的時(shí)候一般是包在一個(gè)函數(shù)中,在需要的時(shí)候去運(yùn)行這個(gè)函數(shù),如:
function runAsync(){ var p = new Promise(function(resolve, reject){ //做一些異步操作 setTimeout(function(){ console.log("執(zhí)行完成"); resolve("隨便什么數(shù)據(jù)"); }, 2000); }); return p; } runAsync()
在上面的代碼中,我們包裝好的函數(shù)最后會(huì)return一個(gè)promise對(duì)象,也就是說promise是一個(gè)對(duì)象,接下來就是promise的一些方法了:then/ catch /all/ race等
首先了解一下沒有promise的回調(diào)地獄
setTimeout(function(){ left(function(){ setTimeout(function(){ left(function(){ setTimeout(function(){ left(); },2000); }); }, 2000); }); }, 2000);
以上代碼就是傳說中的回調(diào)地獄,如果有多層業(yè)務(wù)邏輯嵌套的話,不僅會(huì)使代碼閱讀困難,而且后面維護(hù)起來也是難點(diǎn)。
之后在ES6,Promise就應(yīng)運(yùn)而生。
用then來進(jìn)行回調(diào)操作(鏈?zhǔn)讲僮鳎?/strong>
function runAsync1(){ var p = new Promise(function(resolve, reject){ //做一些異步操作 setTimeout(function(){ console.log("異步任務(wù)1執(zhí)行完成"); resolve("隨便什么數(shù)據(jù)1"); }, 1000); }); return p; } function runAsync2(){ var p = new Promise(function(resolve, reject){ //做一些異步操作 setTimeout(function(){ console.log("異步任務(wù)2執(zhí)行完成"); resolve("隨便什么數(shù)據(jù)2"); }, 2000); }); return p; } function runAsync3(){ var p = new Promise(function(resolve, reject){ //做一些異步操作 setTimeout(function(){ console.log("異步任務(wù)3執(zhí)行完成"); resolve("隨便什么數(shù)據(jù)3"); }, 2000); }); return p; } runAsync1() .then(function(data){ console.log(data); return runAsync2(); }) .then(function(data){ console.log(data); return runAsync3(); }) .then(function(data){ console.log(data); });
這樣能夠按順序,每隔兩秒輸出每個(gè)異步回調(diào)中的內(nèi)容,在runAsync2中傳給resolve的數(shù)據(jù),能在接下來的then方法中拿到。運(yùn)行結(jié)果
在then方法中,你也可以直接return數(shù)據(jù)而不是Promise對(duì)象,在后面的then中就可以接收到數(shù)據(jù)了,比如我們把上面的代碼修改成這樣:
runAsync1() .then(function(data){ console.log(data); return runAsync2(); }) .then(function(data){ console.log(data); return "直接返回?cái)?shù)據(jù)"; //這里直接返回?cái)?shù)據(jù) }) .then(function(data){ console.log(data); });
結(jié)果如下:
接下來說一下reject的用法:
reject的作用就是把Promise的狀態(tài)置為rejected,這樣我們?cè)趖hen中就能捕捉到,然后執(zhí)行“失敗”情況的回調(diào)??聪旅娴拇a。
function getNumber(){ var p = new Promise(function(resolve, reject){ //做一些異步操作 setTimeout(function(){ var num = Math.ceil(Math.random()*10); //生成1-10的隨機(jī)數(shù) if(num<=5){ resolve(num); } else{ reject("數(shù)字太大了"); } }, 2000); }); return p; } getNumber() .then( function(data){ console.log("resolved"); console.log(data); }, function(reason, data){ console.log("rejected"); console.log(reason); } );
getNumber函數(shù)用來異步獲取一個(gè)數(shù)字,2秒后執(zhí)行完成,如果數(shù)字小于等于5,我們認(rèn)為是“成功”了,調(diào)用resolve修改Promise的狀態(tài)。否則我們認(rèn)為是“失敗”了,調(diào)用reject并傳遞一個(gè)參數(shù),作為失敗的原因。
運(yùn)行g(shù)etNumber并且在then中傳了兩個(gè)參數(shù),then方法可以接受兩個(gè)參數(shù),第一個(gè)對(duì)應(yīng)resolve的回調(diào),第二個(gè)對(duì)應(yīng)reject的回調(diào)。所以我們能夠分別拿到他們傳過來的數(shù)據(jù)。多次運(yùn)行這段代碼,你會(huì)隨機(jī)得到下面兩種結(jié)果:
注意,Promise.reject()方法的參數(shù),會(huì)原封不動(dòng)地作為reject的理由,變成后續(xù)方法的參數(shù)。這一點(diǎn)與Promise.resolve方法不一致。
catch方法
catch的用法和then的第二個(gè)參數(shù)一樣,用來指定reject的回調(diào)
getNumber() .then(function(data){ console.log("resolved"); console.log(data); }) .catch(function(reason){ console.log("rejected"); console.log(reason); });
效果和寫在then的第二個(gè)參數(shù)里面一樣。不過它還有另外一個(gè)作用:在執(zhí)行resolve的回調(diào)(也就是上面then中的第一個(gè)參數(shù))時(shí),如果拋出異常了(代碼出錯(cuò)了),那么并不會(huì)報(bào)錯(cuò)卡死js,而是會(huì)進(jìn)到這個(gè)catch方法中。請(qǐng)看下面的代碼:
getNumber() .then(function(data){ console.log("resolved"); console.log(data); console.log(somedata); //此處的somedata未定義 }) .catch(function(reason){ console.log("rejected"); console.log(reason); });
在resolve的回調(diào)中,我們console.log(somedata);而somedata這個(gè)變量是沒有被定義的。如果我們不用Promise,代碼運(yùn)行到這里就直接在控制臺(tái)報(bào)錯(cuò)了,不往下運(yùn)行了。但是在這里,會(huì)得到這樣的結(jié)果:
關(guān)于catch
1.catch是用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)。(建議不要在then的第二個(gè)參數(shù)寫rejected狀態(tài),總是使用catch)
2.catch()使回調(diào)報(bào)錯(cuò)時(shí)不會(huì)卡死js而是會(huì)繼續(xù)往下執(zhí)行。
3.Promise 對(duì)象的錯(cuò)誤具有“冒泡”性質(zhì),會(huì)一直向后傳遞,直到被捕獲為止。也就是說,錯(cuò)誤總是會(huì)被下一個(gè)catch語句捕獲。
這里要注意,不管是then或者catch返回的都是一個(gè)新的Promise實(shí)例!而每個(gè)Primise實(shí)例都有最原始的Pending(進(jìn)行中)到Resolve(已完成),或者Pending(進(jìn)行中)到Reject(已失?。┑倪^程。
all的用法
Promise的all方法提供了并行執(zhí)行異步操作的能力,并且在所有異步操作執(zhí)行完后才執(zhí)行回調(diào)。我們?nèi)耘f使用上面定義好的runAsync1、runAsync2、runAsync3這三個(gè)函數(shù),看下面的例子:
Promise .all([runAsync1(), runAsync2(), runAsync3()]) .then(function(results){ console.log(results); });
用Promise.all來執(zhí)行,all接收一個(gè)數(shù)組參數(shù),里面的值最終都算返回Promise對(duì)象。這樣,三個(gè)異步操作的并行執(zhí)行的,等到它們都執(zhí)行完后才會(huì)進(jìn)到then里面。那么,三個(gè)異步操作返回的數(shù)據(jù)哪里去了呢?都在then里面呢,all會(huì)把所有異步操作的結(jié)果放進(jìn)一個(gè)數(shù)組中傳給then,就是上面的results。所以上面代碼的輸出結(jié)果就是:「誰跑的慢,以誰為準(zhǔn)執(zhí)行回調(diào)」
有了all,你就可以并行執(zhí)行多個(gè)異步操作,并且在一個(gè)回調(diào)中處理所有的返回?cái)?shù)據(jù),有一個(gè)場(chǎng)景是很適合用這個(gè)的,一些游戲類的素材比較多的應(yīng)用,打開網(wǎng)頁時(shí),預(yù)先加載需要用到的各種資源如圖片、flash以及各種靜態(tài)文件。所有的都加載完后,我們?cè)龠M(jìn)行頁面的初始化。(個(gè)人感覺很想&&符號(hào)鏈接)
race的用法
all方法的效果實(shí)際上是「誰跑的慢,以誰為準(zhǔn)執(zhí)行回調(diào)」,那么相對(duì)的就有另一個(gè)方法「誰跑的快,以誰為準(zhǔn)執(zhí)行回調(diào)」,這就是race方法,這個(gè)詞本來就是賽跑的意思。race的用法與all一樣,我們把上面runAsync1的延時(shí)改為1秒來看一下:
Promise .race([runAsync1(), runAsync2(), runAsync3()]) .then(function(results){ console.log(results); });
這三個(gè)異步操作同樣是并行執(zhí)行的。結(jié)果你應(yīng)該可以猜到,1秒后runAsync1已經(jīng)執(zhí)行完了,此時(shí)then里面的就執(zhí)行了。結(jié)果是這樣的:
在then里面的回調(diào)開始執(zhí)行時(shí),runAsync2()和runAsync3()并沒有停止,仍舊再執(zhí)行。于是再過1秒后,輸出了他們結(jié)束的標(biāo)志,resolve/reject一旦輸出不會(huì)改變。(感覺就是||符號(hào)操作~~~)
還有一些關(guān)于promise的面試題。感興趣的可以看一下:https://mp.weixin.qq.com/s/Wv...
以上感謝:王漢炎 枸杞辣條 IT平頭哥聯(lián)盟
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/100849.html
摘要:所謂異步編程中的異步是相對(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)的朋友來說,promise可能是es6比較難以掌握的點(diǎn)。首先是很多名詞,比如Promises,es6 Promise,...
摘要:盡管可以讓代碼更加簡(jiǎn)潔易讀,但對(duì)于只熟悉回調(diào)函數(shù)的人來說,可能對(duì)此還是會(huì)有所懷疑。始終避免在或使用回調(diào)函數(shù),否則會(huì)吞噬任何后續(xù)的錯(cuò)誤,將其作為鏈的一部分。然而,使用回調(diào)函數(shù),使用所謂的,即第一個(gè)參數(shù)是一個(gè)錯(cuò)誤回調(diào)變得很常見。 原文:ES6 Promises: Patterns and Anti-Patterns作者:Bobby Brennan 當(dāng)幾年前,第一次使用 NodeJS 的時(shí)候...
摘要:和和都有和,但是略有不同。實(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ù)處理,的語法糖,和的差不多但不同。 Deferred 和 Promise ES6 和 jQuery 都有 Deffered 和 Promise,但是略有不同。不過它們的作用可以簡(jiǎn)單的用兩句話來描述 Deffere...
摘要:值的情況返回的會(huì)成為狀態(tài)。再次重復(fù)這一句話為的語法糖,它是的別名。也就是說,也是,它用于捕獲錯(cuò)誤,它的參數(shù)也就是是的第二個(gè)參數(shù)。所以,假設(shè)中如果值的話,新的對(duì)象也會(huì)是接受狀態(tài)。 以下是在學(xué)習(xí)Promise關(guān)于catch與then的疑惑總結(jié) 一.catch為then的語法糖 故then方法與catch方法均會(huì)返回一個(gè)Promise對(duì)象(對(duì),即使return 為某個(gè)值,或者throw er...
摘要:構(gòu)造函數(shù)規(guī)定,對(duì)象是一個(gè)構(gòu)造函數(shù),用來生成實(shí)例。如果中的回調(diào)函數(shù)拋出一個(gè)錯(cuò)誤,那么返回的將會(huì)成為拒絕狀態(tài),并且將拋出的錯(cuò)誤作為拒絕狀態(tài)的回調(diào)函數(shù)的參數(shù)值。 其實(shí)想寫 Promise 的使用已經(jīng)很長(zhǎng)時(shí)間了。一個(gè)是在實(shí)際編碼的過程中經(jīng)常用到,一個(gè)是確實(shí)有時(shí)候小伙伴們?cè)谑褂脮r(shí)也會(huì)遇到一些問題。Promise 也確實(shí)是 ES6 中 對(duì)于寫 JS 的方式,有著真正最大影響的 API 特性之一。本...
閱讀 2022·2021-11-24 09:39
閱讀 1884·2019-08-30 15:55
閱讀 2177·2019-08-30 15:53
閱讀 576·2019-08-29 13:16
閱讀 991·2019-08-26 12:20
閱讀 2390·2019-08-26 11:58
閱讀 3155·2019-08-26 10:19
閱讀 3314·2019-08-23 18:31