摘要:第一個(gè)回調(diào)函數(shù)完成以后,會(huì)將返回結(jié)果作為參數(shù),傳入第二個(gè)回調(diào)函數(shù)。捕獲錯(cuò)誤方法是的別名,用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)。處理前一個(gè)回調(diào)函數(shù)運(yùn)行時(shí)發(fā)生的錯(cuò)誤出錯(cuò)啦對(duì)象的錯(cuò)誤具有冒泡性質(zhì),會(huì)一直向后傳遞,直到被捕獲為止。
前言
一直想寫(xiě)一篇關(guān)于promise的文來(lái)總結(jié)一下之前零零散散的promise知識(shí)點(diǎn),趁著工作閑暇,來(lái)做個(gè)總結(jié)。PS:本文適合有一定JavaScript基礎(chǔ)的童鞋閱讀。
什么是promise如果說(shuō)到JavaScript的異步處理,我想大多數(shù)人都會(huì)想到利用回調(diào)函數(shù):
//示例:使用了回調(diào)函數(shù)的異步處理 http.get("/v1/get", function(error, data) { if (error) { //錯(cuò)誤時(shí)的處理 } //成功時(shí)的處理 })
像上面這樣基于回調(diào)函數(shù)的異步處理如果統(tǒng)一參數(shù)使用規(guī)則的話,寫(xiě)法也會(huì)很明了。但是,這也僅是編碼規(guī)約而已,即使采用不同的寫(xiě)法也不會(huì)報(bào)錯(cuò)。
而Promise則是把類似的異步處理對(duì)象和處理規(guī)則進(jìn)行規(guī)范化,并按照采用統(tǒng)一的接口來(lái)編寫(xiě),而采取規(guī)定方法之外的寫(xiě)法都會(huì)報(bào)錯(cuò)。下面看一個(gè)例子:
var promise = http.get("/v1/get"); promise.then(function(result) { //成功時(shí)的處理 }).catch(function(error) { //錯(cuò)誤時(shí)的處理 })
可以看到,這里在使用promise進(jìn)行一步處理的時(shí)候,我們必須按照接口規(guī)定的方法編寫(xiě)處理代碼。也就是說(shuō),除promise對(duì)象規(guī)定的方法(這里的 then 或 catch)以外的方法都是不可以使用的, 而不會(huì)像回調(diào)函數(shù)方式那樣可以自己自由的定義回調(diào)函數(shù)的參數(shù),而必須嚴(yán)格遵守固定、統(tǒng)一的編程方式來(lái)編寫(xiě)代碼。這樣,基于Promise的統(tǒng)一接口的做法, 就可以形成基于接口的各種各樣的異步處理模式。
但這并不是使用promise的足夠理由,promise為異步操作提供了統(tǒng)一的接口,能讓代碼不至于陷入回調(diào)嵌套的死路中,它的強(qiáng)大之處在于它的鏈?zhǔn)秸{(diào)用(文章后面會(huì)有提及)。
基本用法promise的語(yǔ)法:
new Promise(function(resolve, reject) { //待處理的異步邏輯 //處理結(jié)束后,調(diào)用resolve或reject方法 })
新建一個(gè)promise很簡(jiǎn)單,只需要new一個(gè)promise對(duì)象即可。所以promise本質(zhì)上就是一個(gè)函數(shù),它接受一個(gè)函數(shù)作為參數(shù),并且會(huì)返回promise對(duì)象,這就給鏈?zhǔn)秸{(diào)用提供了基礎(chǔ)。
其實(shí)Promise函數(shù)的使命,就是構(gòu)建出它的實(shí)例,并且負(fù)責(zé)幫我們管理這些實(shí)例。而這些實(shí)例有以下三種狀態(tài):
pending: 初始狀態(tài),位履行或拒絕
fulfilled: 意味著操作成功完成
rejected: 意味著操作失敗
pending 狀態(tài)的 Promise對(duì)象可能以 fulfilled狀態(tài)返回了一個(gè)值,也可能被某種理由(異常信息)拒絕(reject)了。當(dāng)其中任一種情況出現(xiàn)時(shí),Promise 對(duì)象的 then 方法綁定的處理方法(handlers)就會(huì)被調(diào)用,then方法分別指定了resolve方法和reject方法的回調(diào)函數(shù)
一圖勝千言:
簡(jiǎn)單的示例:
var promise = new Promise(function(resolve, reject) { if (/* 異步操作成功 */){ resolve(value); } else { reject(error); } }); promise.then(function(value) { // 如果調(diào)用了resolve方法,執(zhí)行此函數(shù) }, function(value) { // 如果調(diào)用了reject方法,執(zhí)行此函數(shù) });
上述代碼很清晰的展示了promise對(duì)象運(yùn)行的機(jī)制。下面再看一個(gè)示例:
var getJSON = function(url) { var promise = new Promise(function(resolve, reject){ var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.responseType = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler() { if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } }; }); return promise; }; getJSON("/posts.json").then(function(json) { console.log("Contents: " + json); }, function(error) { console.error("出錯(cuò)了", error); });
上面代碼中,resolve方法和reject方法調(diào)用時(shí),都帶有參數(shù)。它們的參數(shù)會(huì)被傳遞給回調(diào)函數(shù)。reject方法的參數(shù)通常是Error對(duì)象的實(shí)例,而resolve方法的參數(shù)除了正常的值以外,還可能是另一個(gè)Promise實(shí)例,比如像下面這樣。
var p1 = new Promise(function(resolve, reject){ // ... some code }); var p2 = new Promise(function(resolve, reject){ // ... some code resolve(p1); })
上面代碼中,p1和p2都是Promise的實(shí)例,但是p2的resolve方法將p1作為參數(shù),這時(shí)p1的狀態(tài)就會(huì)傳遞給p2。如果調(diào)用的時(shí)候,p1的狀態(tài)是pending,那么p2的回調(diào)函數(shù)就會(huì)等待p1的狀態(tài)改變;如果p1的狀態(tài)已經(jīng)是fulfilled或者rejected,那么p2的回調(diào)函數(shù)將會(huì)立刻執(zhí)行。
promise的鏈?zhǔn)讲僮?/b>正如前面提到的,Promise.prototype.then方法返回的是一個(gè)新的Promise對(duì)象,因此可以采用鏈?zhǔn)綄?xiě)法。
getJSON("/visa.json").then(function(json) { return json.name; }).then(function(name) { // proceed });
上面的代碼使用then方法,依次指定了兩個(gè)回調(diào)函數(shù)。第一個(gè)回調(diào)函數(shù)完成以后,會(huì)將返回結(jié)果作為參數(shù),傳入第二個(gè)回調(diào)函數(shù)。
如果前一個(gè)回調(diào)函數(shù)返回的是Promise對(duì)象,這時(shí)后一個(gè)回調(diào)函數(shù)就會(huì)等待該P(yáng)romise對(duì)象有了運(yùn)行結(jié)果,才會(huì)進(jìn)一步調(diào)用。
getJSON("/visa/get.json").then(function(post) { return getJSON(post.jobURL); }).then(function(jobs) { // 對(duì)jobs進(jìn)行處理 });
這種設(shè)計(jì)使得嵌套的異步操作,可以被很容易得改寫(xiě),從回調(diào)函數(shù)的“橫向發(fā)展”改為“向下發(fā)展”。
promise捕獲錯(cuò)誤Promise.prototype.catch方法是Promise.prototype.then(null, rejection)的別名,用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)。
getJSON("/visa.json").then(function(result) { // some code }).catch(function(error) { // 處理前一個(gè)回調(diào)函數(shù)運(yùn)行時(shí)發(fā)生的錯(cuò)誤 console.log("出錯(cuò)啦!", error); });
Promise對(duì)象的錯(cuò)誤具有“冒泡”性質(zhì),會(huì)一直向后傳遞,直到被捕獲為止。也就是說(shuō),錯(cuò)誤總是會(huì)被下一個(gè)catch語(yǔ)句捕獲。
getJSON("/visa.json").then(function(json) { return json.name; }).then(function(name) { // proceed }).catch(function(error) { //處理前面任一個(gè)then函數(shù)拋出的錯(cuò)誤 });其他常用的promise方法 Promise.all方法,Promise.race方法
Promise.all方法用于將多個(gè)Promise實(shí)例,包裝成一個(gè)新的Promise實(shí)例。
var p = Promise.all([p1,p2,p3]);
上面代碼中,Promise.all方法接受一個(gè)數(shù)組作為參數(shù),p1、p2、p3都是Promise對(duì)象的實(shí)例。(Promise.all方法的參數(shù)不一定是數(shù)組,但是必須具有iterator接口,且返回的每個(gè)成員都是Promise實(shí)例。)
p的狀態(tài)由p1、p2、p3決定,分成兩種情況。
只有p1、p2、p3的狀態(tài)都變成fulfilled,p的狀態(tài)才會(huì)變成fulfilled,此時(shí)p1、p2、p3的返回值組成一個(gè)數(shù)組,傳遞給p的回調(diào)函數(shù)。
只要p1、p2、p3之中有一個(gè)被rejected,p的狀態(tài)就變成rejected,此時(shí)第一個(gè)被reject的實(shí)例的返回值,會(huì)傳遞給p的回調(diào)函數(shù)。
下面是一個(gè)具體的例子。
// 生成一個(gè)Promise對(duì)象的數(shù)組 var promises = [2, 3, 5, 7, 11, 13].map(function(id){ return getJSON("/get/addr" + id + ".json"); }); Promise.all(promises).then(function(posts) { // ... }).catch(function(reason){ // ... });
Promise.race方法同樣是將多個(gè)Promise實(shí)例,包裝成一個(gè)新的Promise實(shí)例。
var p = Promise.race([p1,p2,p3]);
上面代碼中,只要p1、p2、p3之中有一個(gè)實(shí)例率先改變狀態(tài),p的狀態(tài)就跟著改變。那個(gè)率先改變的Promise實(shí)例的返回值,就傳遞給p的返回值。
如果Promise.all方法和Promise.race方法的參數(shù),不是Promise實(shí)例,就會(huì)先調(diào)用下面講到的Promise.resolve方法,將參數(shù)轉(zhuǎn)為Promise實(shí)例,再進(jìn)一步處理。
Promise.resolve方法,Promise.reject方法有時(shí)需要將現(xiàn)有對(duì)象轉(zhuǎn)為Promise對(duì)象,Promise.resolve方法就起到這個(gè)作用。
var jsPromise = Promise.resolve($.ajax("/whatever.json"));
上面代碼將jQuery生成deferred對(duì)象,轉(zhuǎn)為一個(gè)新的ES6的Promise對(duì)象。
如果Promise.resolve方法的參數(shù),不是具有then方法的對(duì)象(又稱thenable對(duì)象),則返回一個(gè)新的Promise對(duì)象,且它的狀態(tài)為fulfilled。
var p = Promise.resolve("Hello"); p.then(function (s){ console.log(s) }); // Hello
上面代碼生成一個(gè)新的Promise對(duì)象的實(shí)例p,它的狀態(tài)為fulfilled,所以回調(diào)函數(shù)會(huì)立即執(zhí)行,Promise.resolve方法的參數(shù)就是回調(diào)函數(shù)的參數(shù)。
如果Promise.resolve方法的參數(shù)是一個(gè)Promise對(duì)象的實(shí)例,則會(huì)被原封不動(dòng)地返回。
Promise.reject(reason)方法也會(huì)返回一個(gè)新的Promise實(shí)例,該實(shí)例的狀態(tài)為rejected。Promise.reject方法的參數(shù)reason,會(huì)被傳遞給實(shí)例的回調(diào)函數(shù)。
var p = Promise.reject("出錯(cuò)啦"); p.then(null, function (error){ console.log(error) }); // 出錯(cuò)了
上面代碼生成一個(gè)Promise對(duì)象的實(shí)例p,狀態(tài)為rejected,回調(diào)函數(shù)會(huì)立即執(zhí)行。
題外話:async函數(shù)async函數(shù)是es7提案出來(lái)的語(yǔ)法,并不屬于es6,但是已經(jīng)有一些平臺(tái)和編輯器支持這種函數(shù)了,所以這里也做一下了解。
async函數(shù)是用來(lái)取代回調(diào)函數(shù)的另一種方法。
只要函數(shù)名之前加上async關(guān)鍵字,就表明該函數(shù)內(nèi)部有異步操作。該異步操作應(yīng)該返回一個(gè)Promise對(duì)象,前面用await關(guān)鍵字注明。當(dāng)函數(shù)執(zhí)行的時(shí)候,一旦遇到await就會(huì)先返回,等到觸發(fā)的異步操作完成,再接著執(zhí)行函數(shù)體內(nèi)后面的語(yǔ)句。
async function getStockPrice(symbol, currency) { let price = await getStockPrice(symbol); return convert(price, currency); }
上面代碼是一個(gè)獲取股票報(bào)價(jià)的函數(shù),函數(shù)前面的async關(guān)鍵字,表明該函數(shù)將返回一個(gè)Promise對(duì)象。調(diào)用該函數(shù)時(shí),當(dāng)遇到await關(guān)鍵字,立即返回它后面的表達(dá)式(getStockPrice函數(shù))產(chǎn)生的Promise對(duì)象,不再執(zhí)行函數(shù)體內(nèi)后面的語(yǔ)句。等到getStockPrice完成,再自動(dòng)回到函數(shù)體內(nèi),執(zhí)行剩下的語(yǔ)句。
下面是一個(gè)更一般性的例子。
function timeout(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } async function asyncValue(value) { await timeout(50); return value; }
上面代碼中,asyncValue函數(shù)前面有async關(guān)鍵字,表明函數(shù)體內(nèi)有異步操作。執(zhí)行的時(shí)候,遇到await語(yǔ)句就會(huì)先返回,等到timeout函數(shù)執(zhí)行完畢,再返回value。
個(gè)人覺(jué)得async函數(shù)將異步發(fā)揮到了極致,代碼看上去更加簡(jiǎn)潔,更加舒服了,而且其流程也很好理解。
總結(jié)promise作為異步操作的規(guī)則,確實(shí)給開(kāi)發(fā)帶來(lái)了不少便利,至少不用像回調(diào)那樣,出現(xiàn)函數(shù)里面套函數(shù)這種無(wú)限的嵌套的情況,promise讓你的代碼變得更加的優(yōu)雅了。當(dāng)然如果之后async函數(shù)變得更加普及,那么就更好了。
下面來(lái)看看下面這道題目,大家可以思考下結(jié)果是多少?
console.log(1); new Promise(function (resolve, reject){ reject(true); window.setTimeout(function (){ resolve(false); }, 0); }).then(function(){ console.log(2); }, function(){ console.log(3); }); console.log(4);
答案我就不貼出來(lái)了,給大家思考的空間,實(shí)在不知道結(jié)果的也可以直接復(fù)制這段代碼到瀏覽器控制臺(tái)執(zhí)行下,也能很快的出結(jié)果。
展望很好奇promise內(nèi)部的實(shí)現(xiàn)機(jī)理,接下來(lái)我會(huì)深入研究下promise實(shí)現(xiàn)原理,有成果了給大家分享哦~
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/83035.html
摘要:下一篇大概就是源碼方面的學(xué)習(xí)筆記了龜速學(xué)習(xí)中這一次我是去看了下規(guī)范照例傳送門(mén)圖靈社區(qū)規(guī)范首先吧個(gè)人總結(jié)下該用的詞解決結(jié)婚拒絕婉拒終值值傳家寶拒因好人卡等等異常車禍理下概念我們的的就像是一場(chǎng)姻緣對(duì)吧解決呢就是結(jié)婚成功啦傳家寶也如愿的傳給下一代 下一篇大概就是源碼方面的學(xué)習(xí)筆記了...龜速學(xué)習(xí)中... 這一次我是去看了下Promises/A+規(guī)范照例傳送門(mén):圖靈社區(qū)Promises/A+規(guī)...
摘要:版本以及之前,本身還沒(méi)有異步執(zhí)行代碼的能力,宿主環(huán)境傳遞給引擎,然后按順序執(zhí)行,由宿主發(fā)起任務(wù)。采納引擎術(shù)語(yǔ),把宿主發(fā)起的任務(wù)稱為宏觀任務(wù),把引擎發(fā)起的任務(wù)稱為微觀任務(wù)?;居梅ㄊ纠幕卣{(diào)是一個(gè)異步的執(zhí)行過(guò)程。 筆記說(shuō)明 重學(xué)前端是程劭非(winter)【前手機(jī)淘寶前端負(fù)責(zé)人】在極客時(shí)間開(kāi)的一個(gè)專欄,每天10分鐘,重構(gòu)你的前端知識(shí)體系,筆者主要整理學(xué)習(xí)過(guò)程的一些要點(diǎn)筆記以及感悟,完整的...
摘要:版本以及之前,本身還沒(méi)有異步執(zhí)行代碼的能力,宿主環(huán)境傳遞給引擎,然后按順序執(zhí)行,由宿主發(fā)起任務(wù)。采納引擎術(shù)語(yǔ),把宿主發(fā)起的任務(wù)稱為宏觀任務(wù),把引擎發(fā)起的任務(wù)稱為微觀任務(wù)?;居梅ㄊ纠幕卣{(diào)是一個(gè)異步的執(zhí)行過(guò)程。 筆記說(shuō)明 重學(xué)前端是程劭非(winter)【前手機(jī)淘寶前端負(fù)責(zé)人】在極客時(shí)間開(kāi)的一個(gè)專欄,每天10分鐘,重構(gòu)你的前端知識(shí)體系,筆者主要整理學(xué)習(xí)過(guò)程的一些要點(diǎn)筆記以及感悟,完整的...
摘要:版本以及之前,本身還沒(méi)有異步執(zhí)行代碼的能力,宿主環(huán)境傳遞給引擎,然后按順序執(zhí)行,由宿主發(fā)起任務(wù)。采納引擎術(shù)語(yǔ),把宿主發(fā)起的任務(wù)稱為宏觀任務(wù),把引擎發(fā)起的任務(wù)稱為微觀任務(wù)?;居梅ㄊ纠幕卣{(diào)是一個(gè)異步的執(zhí)行過(guò)程。 筆記說(shuō)明 重學(xué)前端是程劭非(winter)【前手機(jī)淘寶前端負(fù)責(zé)人】在極客時(shí)間開(kāi)的一個(gè)專欄,每天10分鐘,重構(gòu)你的前端知識(shí)體系,筆者主要整理學(xué)習(xí)過(guò)程的一些要點(diǎn)筆記以及感悟,完整的...
摘要:異步操作未完成異步操作成功異步操作失敗基本用法是一個(gè)構(gòu)造函數(shù),接收一個(gè)參數(shù),這個(gè)參數(shù)是函數(shù),同時(shí)這個(gè)參數(shù)函數(shù)要傳入兩個(gè)參數(shù),,分別表示異步操作執(zhí)行成功后的回調(diào)函數(shù)和異步操作執(zhí)行失敗后的回調(diào)函數(shù)。如果調(diào)用函數(shù),就會(huì)調(diào)用方法的第一個(gè)參數(shù)。 Promise對(duì)象 Promise 表示一個(gè)異步操作的最終結(jié)果,與之進(jìn)行交互的方式主要是 then 方法,該方法注冊(cè)了兩個(gè)回調(diào)函數(shù),用于接收 promi...
閱讀 856·2023-04-25 21:21
閱讀 3237·2021-11-24 09:39
閱讀 3079·2021-09-02 15:41
閱讀 2009·2021-08-26 14:13
閱讀 1839·2019-08-30 11:18
閱讀 2786·2019-08-29 16:25
閱讀 517·2019-08-28 18:27
閱讀 1590·2019-08-28 18:17