你是怎么理解ES6中 Promise的?使用場景?

一、介紹

Promise,譯為承諾,是異步編程的一種解決方案,比傳統(tǒng)的解決方案(回調(diào)函數(shù))更加合理和更加強大

在以往我們?nèi)绻幚矶鄬赢惒讲僮鳎覀兺鶗裣旅婺菢泳帉懳覀兊拇a

doSomething(function(result) {? doSomethingElse(result, function(newResult) {??? doThirdThing(newResult, function(finalResult) {????? console.log('得到最終結(jié)果: ' + finalResult);??? }, failureCallback);? }, failureCallback);}, failureCallback);

閱讀上面代碼,是不是很難受,上述形成了經(jīng)典的回調(diào)地獄

現(xiàn)在通過Promise的改寫上面的代碼

doSomething().then(function(result) {? return doSomethingElse(result);}).then(function(newResult) {? return doThirdThing(newResult);}).then(function(finalResult) {? console.log('得到最終結(jié)果: ' + finalResult);}).catch(failureCallback);

瞬間感受到promise解決異步操作的優(yōu)點:

[if !supportLists]?????????????[endif]鏈?zhǔn)讲僮鳒p低了編碼難度

[if !supportLists]?????????????[endif]代碼可讀性明顯增強

下面我們正式來認(rèn)識promise:

狀態(tài)

promise對象僅有三種狀態(tài)

[if !supportLists]?????????????[endif]pending(進行中)

[if !supportLists]?????????????[endif]fulfilled(已成功)

[if !supportLists]?????????????[endif]rejected(已失敗)

特點

[if !supportLists]?????????????[endif]對象的狀態(tài)不受外界影響,只有異步操作的結(jié)果,可以決定當(dāng)前是哪一種狀態(tài)

[if !supportLists]?????????????[endif]一旦狀態(tài)改變(從pending變?yōu)閒ulfilled和從pending變?yōu)閞ejected),就不會再變,任何時候都可以得到這個結(jié)果

流程

認(rèn)真閱讀下圖,我們能夠輕松了解promise整個流程

[if !vml]

[endif]

二、用法

Promise對象是一個構(gòu)造函數(shù),用來生成Promise實例

const promise = new Promise(function(resolve, reject) {});

Promise構(gòu)造函數(shù)接受一個函數(shù)作為參數(shù),該函數(shù)的兩個參數(shù)分別是resolve和reject

[if !supportLists]?????????????[endif]resolve函數(shù)的作用是,將Promise對象的狀態(tài)從“未完成”變?yōu)椤俺晒Α?/p>

[if !supportLists]?????????????[endif]reject函數(shù)的作用是,將Promise對象的狀態(tài)從“未完成”變?yōu)椤笆 ?/p>

實例方法

Promise構(gòu)建出來的實例存在以下方法:

[if !supportLists]?????????????[endif]then()

[if !supportLists]?????????????[endif]catch()

[if !supportLists]?????????????[endif]finally()

then()

then是實例狀態(tài)發(fā)生改變時的回調(diào)函數(shù),第一個參數(shù)是resolved狀態(tài)的回調(diào)函數(shù),第二個參數(shù)是rejected狀態(tài)的回調(diào)函數(shù)

then方法返回的是一個新的Promise實例,也就是promise能鏈?zhǔn)綍鴮懙脑?/p>

getJSON("/posts.json").then(function(json) {? return json.post;}).then(function(post) {? // ...});

catch

catch()方法是.then(null,

rejection)或.then(undefined, rejection)的別名,用于指定發(fā)生錯誤時的回調(diào)函數(shù)

getJSON('/posts.json').then(function(posts) {? // ...}).catch(function(error) {? // 處理getJSON 前一個回調(diào)函數(shù)運行時發(fā)生的錯誤? console.log('發(fā)生錯誤!', error);});

Promise對象的錯誤具有“冒泡”性質(zhì),會一直向后傳遞,直到被捕獲為止

getJSON('/post/1.json').then(function(post) {? return getJSON(post.commentURL);}).then(function(comments) {? // some code}).catch(function(error) {? // 處理前面三個Promise產(chǎn)生的錯誤});

一般來說,使用catch方法代替then()第二個參數(shù)

Promise對象拋出的錯誤不會傳遞到外層代碼,即不會有任何反應(yīng)

const someAsyncThing = function() {? return new Promise(function(resolve, reject) {??? // 下面一行會報錯,因為x沒有聲明??? resolve(x + 2);? });};

瀏覽器運行到這一行,會打印出錯誤提示ReferenceError: x is not defined,但是不會退出進程

catch()方法之中,還能再拋出錯誤,通過后面catch方法捕獲到

finally()

finally()方法用于指定不管 Promise 對象最后狀態(tài)如何,都會執(zhí)行的操作

promise.then(result => {···}).catch(error => {···}).finally(() => {···});

構(gòu)造函數(shù)方法

Promise構(gòu)造函數(shù)存在以下方法:

[if !supportLists]?????????????[endif]all()

[if !supportLists]?????????????[endif]race()

[if !supportLists]?????????????[endif]allSettled()

[if !supportLists]?????????????[endif]resolve()

[if !supportLists]?????????????[endif]reject()

[if !supportLists]?????????????[endif]try()

all()

Promise.all()方法用于將多個 Promise實例,包裝成一個新的 Promise實例

const p = Promise.all([p1, p2, p3]);

接受一個數(shù)組(迭代對象)作為參數(shù),數(shù)組成員都應(yīng)為Promise實例

實例p的狀態(tài)由p1、p2、p3決定,分為兩種:

[if !supportLists]?????????????[endif]只有p1、p2、p3的狀態(tài)都變成fulfilled,p的狀態(tài)才會變成fulfilled,此時p1、p2、p3的返回值組成一個數(shù)組,傳遞給p的回調(diào)函數(shù)

[if !supportLists]?????????????[endif]只要p1、p2、p3之中有一個被rejected,p的狀態(tài)就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調(diào)函數(shù)

注意,如果作為參數(shù)的 Promise 實例,自己定義了catch方法,那么它一旦被rejected,并不會觸發(fā)Promise.all()的catch方法

const p1 = new Promise((resolve, reject) => {? resolve('hello');}).then(result => result).catch(e => e);

const p2 = new Promise((resolve, reject) => {? throw new Error('報錯了');}).then(result => result).catch(e => e);

Promise.all([p1, p2]).then(result => console.log(result)).catch(e => console.log(e));

//

["hello", Error:

報錯了]

如果p2沒有自己的catch方法,就會調(diào)用Promise.all()的catch方法

const p1 = new Promise((resolve, reject) => {? resolve('hello');}).then(result => result);

const p2 = new Promise((resolve, reject) => {? throw new Error('報錯了');}).then(result => result);

Promise.all([p1, p2]).then(result => console.log(result)).catch(e => console.log(e));

// Error:

報錯了

race()

Promise.race()方法同樣是將多個 Promise 實例,包裝成一個新的 Promise 實例

const p = Promise.race([p1, p2, p3]);

只要p1、p2、p3之中有一個實例率先改變狀態(tài),p的狀態(tài)就跟著改變

率先改變的 Promise 實例的返回值則傳遞給p的回調(diào)函數(shù)

const p = Promise.race([? fetch('/resource-that-may-take-a-while'),? new Promise(function (resolve, reject) {??? setTimeout(() => reject(new Error('request timeout')), 5000)? })]);

p.then(console.log).catch(console.error);

allSettled()

Promise.allSettled()方法接受一組 Promise 實例作為參數(shù),包裝成一個新的Promise 實例

只有等到所有這些參數(shù)實例都返回結(jié)果,不管是fulfilled還是rejected,包裝實例才會結(jié)束

const promises = [? fetch('/api-1'),? fetch('/api-2'),? fetch('/api-3'),];

await Promise.allSettled(promises);removeLoadingIndicator();

resolve()

將現(xiàn)有對象轉(zhuǎn)為 Promise對象

Promise.resolve('foo')

//

等價于new Promise(resolve => resolve('foo'))

參數(shù)可以分成四種情況,分別如下:

[if !supportLists]?????????????[endif]參數(shù)是一個 Promise 實例,promise.resolve將不做任何修改、原封不動地返回這個實例

[if !supportLists]?????????????[endif]參數(shù)是一個thenable對象,promise.resolve會將這個對象轉(zhuǎn)為 Promise對象,然后就立即執(zhí)行thenable對象的then()方法

[if !supportLists]?????????????[endif]參數(shù)不是具有then()方法的對象,或根本就不是對象,Promise.resolve()會返回一個新的 Promise 對象,狀態(tài)為resolved

[if !supportLists]?????????????[endif]沒有參數(shù)時,直接返回一個resolved狀態(tài)的 Promise 對象

reject()

Promise.reject(reason)方法也會返回一個新的 Promise 實例,該實例的狀態(tài)為rejected

const p = Promise.reject('出錯了');

//

等同于const p = new Promise((resolve, reject) => reject('出錯了'))

p.then(null, function (s) {? console.log(s)});

//

出錯了

Promise.reject()方法的參數(shù),會原封不動地變成后續(xù)方法的參數(shù)

Promise.reject('出錯了').catch(e => {? console.log(e === '出錯了')})

// true

三、使用場景

將圖片的加載寫成一個Promise,一旦加載完成,Promise的狀態(tài)就發(fā)生變化

const preloadImage = function (path) {? return new Promise(function (resolve, reject) {??? const image = new Image();??? image.onload? = resolve;??? image.onerror = reject;??? image.src = path;? });};

通過鏈?zhǔn)讲僮鳎瑢⒍鄠€渲染數(shù)據(jù)分別給個then,讓其各司其職。或當(dāng)下個異步請求依賴上個請求結(jié)果的時候,我們也能夠通過鏈?zhǔn)讲僮饔押媒鉀Q問題

// 各司其職getInfo().then(res=>{??? let { bannerList } = res??? //渲染輪播圖??? console.log(bannerList)??? return res}).then(res=>{??? ??? let { storeList } = res??? //渲染店鋪列表??? console.log(storeList)??? return res}).then(res=>{??? let { categoryList } = res??? console.log(categoryList)??? //渲染分類列表??? return res})

通過all()實現(xiàn)多個請求合并在一起,匯總所有請求結(jié)果,只需設(shè)置一個loading即可

function initLoad(){??? // loading.show() //加載loading??? Promise.all([getBannerList(),getStoreList(),getCategoryList()]).then(res=>{??????? console.log(res)??????? loading.hide() //關(guān)閉loading??? }).catch(err=>{??????? console.log(err)??????? loading.hide()//關(guān)閉loading??? })}

//

數(shù)據(jù)初始化??? initLoad()

通過race可以設(shè)置圖片請求超時

//請求某個圖片資源function requestImg(){??? var p = new Promise(function(resolve, reject){??????? var img = new Image();??????? img.onload = function(){?????????? resolve(img);??????? }??????? //img.src =

"https://b-gold-cdn.xitu.io/v3/static/img/logo.a7995ad.svg"; 正確的??????? img.src = "https://b-gold-cdn.xitu.io/v3/static/img/logo.a7995ad.svg1";??? });??? return p;}

//

延時函數(shù),用于給請求計時function timeout(){??? var p = new Promise(function(resolve, reject){??????? setTimeout(function(){??????????? reject('圖片請求超時');??????? }, 5000);??? });??? return p;}

Promise.race([requestImg(), timeout()]).then(function(results){??? console.log(results);}).catch(function(reason){??? console.log(reason);});

參考文獻

[if !supportLists]?????????????[endif]https://es6.ruanyifeng.com/#docs/promise

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內(nèi)容

  • 一、介紹 Promise ,譯為承諾,是異步編程的一種解決方案,比傳統(tǒng)的解決方案(回調(diào)函數(shù))更加合理和更加強大 在...
    JerisonPaul閱讀 738評論 0 0
  • 在ES6當(dāng)中添加了很多新的API其中很值得一提的當(dāng)然少不了Promise,因為Promise的出現(xiàn),很輕松的就給開...
    嘿_那個誰閱讀 3,676評論 2 3
  • 難點一:promise的狀態(tài) promise是一個容器,它具有三種狀態(tài) (進行中)<fulfilled>(成功)<...
    南淮十二刀閱讀 698評論 0 1
  • Promise 對象 1.Promise對象是ES6對異步編程的一種解決方案,它有以下兩個特點: Promise對...
    Cryptic閱讀 1,002評論 1 5
  • 前面的話 JS有很多強大的功能,其中一個是它可以輕松地搞定異步編程。作為一門為Web而生的語言,它從一開始就需要能...
    CodeMT閱讀 624評論 0 0