Promise和async/await(一)

為什么會有同步和異步?

首先JS 是單線程的,單線程程序在執(zhí)行的時候夫凸,所有的程序都是按照順序執(zhí)行的,前面的必須處理好后面的才會執(zhí)行阱持。
JS 是單線程的夭拌,但是瀏覽器加載一些需要網(wǎng)絡(luò)請求的資源,ajax或者執(zhí)行一段 setTimeout代碼衷咽,由于是單線程鸽扁,要等這些內(nèi)容訪問或者執(zhí)行完才執(zhí)行下面的代碼,那么你發(fā)送ajax請求镶骗,執(zhí)行setTimeout 的這段時間什么也做不了桶现,這種效果對程序是一種堵塞(同步堵塞),這個時候異步就出現(xiàn)了卖词,在涉及需要等待的操作巩那,我們把代碼交給其他對應(yīng)的瀏覽器線程去執(zhí)行吏夯,在執(zhí)行結(jié)束的時候此蜈,通知我們的主線程執(zhí)行完畢,你可以操作資源了噪生,這段等待時間并不影響你程序的執(zhí)行裆赵,只是在未來的某個時間段(不確定),有一個操作一定執(zhí)行跺嗽,這就是異步(異步非阻塞)

回調(diào)

異步和同步相比战授,最難掌控的就是異步任務(wù)會什么時候完成和完成之后的回調(diào)問題。
回調(diào)是異步編程最基本的方法
像下面的例子

listen( "click", function handler(evt){
    setTimeout( function request(){
        ajax( "http://some.url.1", function response(text){
            if (text == "hello") {
                handler();
            }
            else if (text == "world") {
                request();
            }
        } );
    }, 500) ;
} );
console.log('doSomething')

這種地域式的回調(diào)桨嫁,令代碼的可讀性非常差V怖肌!

信任問題

在你不知道的javascript一書中璃吧,對于回調(diào)的信任問題做了闡述 當(dāng)你使用第三方的庫的方法處理回調(diào)時很有可能遇到以下信任內(nèi)容
· 調(diào)用回調(diào)的過早
· 調(diào)用回調(diào)過晚
· 調(diào)用回調(diào)次數(shù)過多會過少
· 沒有把所需要的環(huán)境/參數(shù)成功的傳給你的回調(diào)函數(shù)
· 吞掉可能出現(xiàn)的錯誤或異常
· .......
那么怎么解決這種信任問題呢楣导?

你需要一個承諾

當(dāng)你把一件事情交給別人去做的時候,這個任務(wù)可能馬上完成也可能一段時間后完成畜挨,這個人在任務(wù)完成或者失敗的時候回給你一個回應(yīng)筒繁,放心的人!巴元!回應(yīng)就表示成功了或者失敗了毡咏,沒回應(yīng)就表示正在執(zhí)行~~~

Promise(承諾) 就是這樣人

【官方】Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大逮刨。它由社區(qū)最早提出和實(shí)現(xiàn)呕缭,ES6 將其寫進(jìn)了語言標(biāo)準(zhǔn),統(tǒng)一了用法,原生提供了Promise對象
三種狀態(tài):
1.pending(進(jìn)行中)
2.fulfilled (已成功)通常也稱為 resolved
3.rejected (已失敾肿堋)
特點(diǎn)
1.對象狀態(tài)不受外界影響落恼。只有異步操作的結(jié)果,可以決定當(dāng)前是哪一種狀態(tài)离熏,任何其他操作都無法改變這個狀態(tài)
2.一旦改變狀態(tài)佳谦,就不會在改變,任何時候都可以得到這個結(jié)果滋戳。Promise對象的狀態(tài)改變钻蔑,只有兩種可能:從pending變?yōu)閒ulfilled和從pending變?yōu)閞ejected
缺點(diǎn)
1.無法取消Promise,一旦新建它就會立即執(zhí)行奸鸯,無法中途取消咪笑。
2.如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯誤娄涩,不會反應(yīng)到外部窗怒。
3.當(dāng)處于pending狀態(tài)時,無法得知目前進(jìn)展到哪一個階段(剛剛開始還是即將完成)蓄拣。

我們看一個簡單的promise例子

Promise構(gòu)造函數(shù)接受一個函數(shù)扬虚,該函數(shù)有兩個參數(shù) resolve 和 reject,他們也是兩個函數(shù)球恤。
resolve函數(shù)的作用是:將Promise 的狀態(tài)從“未完成”變成“成功”(即從“pending”變成“resolved”)辜昵,在異步操作成功的時調(diào)用,并將一部操作的結(jié)果咽斧,作為參數(shù)傳遞出去堪置。
reject函數(shù)的作用是:將Promise 的狀態(tài)從“未完成”變成“失敗”(即從“pending”變成“rejected”),在異步操作成功的時調(diào)用张惹,并將一部操作的結(jié)果舀锨,作為參數(shù)傳遞出去。
Promise實(shí)例生成以后宛逗,可以用then方法分別指定resolved狀態(tài)和rejected狀態(tài)的回調(diào)函數(shù)坎匿。

let promise = new Promise((resolve,reject)=>{
    // 接收一個callback。參數(shù)是成功函數(shù)與失敗函數(shù)
    setTimeout(()=>{
       let num = parseInt(Math.random()*100);
       // 如果數(shù)字大于50就調(diào)用成功的函數(shù)拧额,并且將狀態(tài)變成Resolved
       if(num > 50){
          resolve(num);
       }else{
        // 否則就調(diào)用失敗的函數(shù)碑诉,將狀態(tài)變成Rejected
          reject(num)
       }
    },3000)
})

當(dāng)Promise執(zhí)行的內(nèi)容符合你預(yù)期的成功條件的話,就調(diào)用resolve函數(shù)侥锦,失敗就調(diào)用reject函數(shù)进栽,這兩個函數(shù)的參數(shù)會被promise捕捉到」Э眩可以在之后的回調(diào)中使用
創(chuàng)建一個承諾完成了快毛,我們?nèi)绾问褂盟?/p>

promise.then(res => {
  //在構(gòu)造函數(shù)中如果你執(zhí)行力resolve函數(shù)就會到這一步
  console.log(res)
}, err => {
  // 執(zhí)行了reject函數(shù)會到這一步
  console.log(err);
})


Promise.prototype.then()

then方法接収兩個函數(shù)格嗅,第一個是承諾成功(狀態(tài)為resolved)的回調(diào)函數(shù),第二個(可選)是承諾失斶氲邸(狀態(tài)為rejected)的回調(diào)函數(shù)屯掖。
then方法的返回值不是一個promise對象就會被包裝成一個promise對象,所以then方法支持鏈?zhǔn)秸{(diào)用襟衰。
then方法的可以幫我們串行的解決一些邏輯問題贴铜,讓我們的書寫更加順暢。
看看下面這個

ajax('first');
ajax('second');
ajax('third');
需要按順序來執(zhí)行怎么辦瀑晒?
ajax('first').success(function(res){
    ajax('second').success(function(res){
        ajax('third').success(function(res){
            //串行完畢可以執(zhí)行你想要的內(nèi)容了
        });
    })
})

上面地獄式的回調(diào)绍坝,可怕!苔悦!如果使用下面的 then 鏈?zhǔn)秸{(diào)用轩褐,就會好很多

let promise = new Promise((resolve,reject)=>{
    ajax('first').success(function(res){
        resolve(res);
    })
})
promise.then(res=>{
    return new Promise((resovle,reject)=>{
        ajax('second').success(function(res){
            resolve(res)
        })
    })
}).then(res=>{
    return new Promise((resovle,reject)=>{
        ajax('second').success(function(res){
            resolve(res)
        })
    })
}).then(res=>{
    // 串行完畢你要做的xxx可以開始了
})

串行說完了,那并行的怎么辦玖详,當(dāng)我們有多個異步事件把介,之間并沒有先后順序,只需要全部完成就可以開始工作蟋座。
這個時候我們可以使用 Promise.all

Promise.all()

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

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

上面代碼中蜈七,Promise.all()方法接受一個數(shù)組作為參數(shù)秒拔,p1、p2飒硅、p3都是 Promise 實(shí)例,如果不是作谚,就會先調(diào)用下面講到的Promise.resolve方法三娩,將參數(shù)轉(zhuǎn)為 Promise 實(shí)例,再進(jìn)一步處理妹懒。另外雀监,Promise.all()方法的參數(shù)可以不是數(shù)組,但必須具有 Iterator 接口眨唬,且返回的每個成員都是 Promise 實(shí)例会前。
p的狀態(tài)由p1、p2匾竿、p3決定瓦宜,分成兩種情況。
1.只有p1岭妖、p2临庇、p3的狀態(tài)都變成fulfilled反璃,p的狀態(tài)才會變成fulfilled,此時p1假夺、p2淮蜈、p3的返回值組成一個數(shù)組,傳遞給p的回調(diào)函數(shù)已卷。
2.只要p1梧田、p2、p3之中有一個被rejected侧蘸,p的狀態(tài)就變成rejected柿扣,此時第一個被reject的實(shí)例的返回值,會傳遞給p的回調(diào)函數(shù)闺魏。

var p1 = new Promise((resolve, reject) => {
    resolve('p1')
})
var p2 = new Promise((resolve, reject) => {
    setTimeout(()=> {
         resolve('p1')
    }, 1000)
})
var p3 = new Promise((resolve, reject) => {
    resolve('p3')
})
Promise.all([p1, p2, p3])
.then(val => {
    console.log('then', val)
})
.catch(err => {
    console.log('catch', err)
})
//   then (3) ["p1", "p1", "p3"]

var p1 = new Promise((resolve, reject) => {
    resolve('p1')
})
var p2 = new Promise((resolve, reject) => {
    setTimeout(()=> {
         resolve('p1')
    }, 1000)
})
var p3 = new Promise((resolve, reject) => {
    reject('p3')
})
Promise.all([p1, p2, p3])
.then(val => {
    console.log('then', val)
})
.catch(err => {
    console.log('catch', err)
})
//  catch p3

注意: 若實(shí)例自帶了 .then() 則先執(zhí)行實(shí)例的 then 在執(zhí)行 all的then未状;若實(shí)例自帶了 .catch 則先調(diào)用 實(shí)例的 catch 再執(zhí)行 .then()。

var p1 = new Promise((resolve, reject) => {
    resolve('p1')
}).then(val => {
    console.log(555, val)
})
var p2 = new Promise((resolve, reject) => {
    setTimeout(()=> {
         resolve('p1')
    }, 1000)
})
var p3 = new Promise((resolve, reject) => {
    resolve('p3')
})
Promise.all([p1, p2, p3])
.then(val => {
    console.log('then', val)
})
.catch(err => {
    console.log('catch', err)
})

// 555 'p1'
// then [undefined, 'p1', 'p3']


Promise.race()

Promise.race()方法同樣是將多個 Promise 實(shí)例析桥,包裝成一個新的 Promise 實(shí)例司草。

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

上面代碼中,只要p1泡仗、p2埋虹、p3之中有一個實(shí)例率先改變狀態(tài),p的狀態(tài)就跟著改變娩怎。那個率先改變的 Promise 實(shí)例的返回值搔课,就傳遞給p的回調(diào)函數(shù)。

Promise.prototype.catch()

Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的別名截亦,用于指定發(fā)生錯誤時的回調(diào)函數(shù)爬泥。

var p = new Promise((resolve, reject) => {
    //此處拋出異常,狀態(tài)就會變成rejected崩瓤,就會調(diào)用catch() 方法的回調(diào)函數(shù)袍啡,處理這個錯誤。
    throw new Error("測試錯誤test!!!")  
})
p.catch((err) => {
    console.log(err)    // Error: 測試錯誤test!!!
})

等同于

// 寫法一
var p = new Promise((resolve, reject) => {
    throw new Error("測試錯誤test!!!")  
})
p.then(null, (err) => {
    console.log(err)
})
// 寫法二
var p = new Promise((resolve, reject) => {
    reject(new Error("錯誤測試test!!!!"))
})
p.catch((err)=> {
    console.log(err)
})
// 寫法三
var p = new Promise((resolve, reject) => {
    try {
       throw new Error("錯誤測試testH赐啊境输!")
    } catch(err) {
        reject(err)
    }
})
p.catch((err)=> {
    console.log(err)
})

Promise 對象的錯誤具有“冒泡”性質(zhì),會一直向后傳遞颖系,直到被捕獲為止嗅剖。也就是說,錯誤總是會被下一個catch語句捕獲嘁扼。
一般來說信粮,不要在then方法里面定義 Reject 狀態(tài)的回調(diào)函數(shù)(即then的第二個參數(shù)),總是使用catch方法比較好偷拔。

// 不建議使用
var p = new Promise((resolve, reject) => {
    throw new Error("test-error")
})
p.then(val=> {
    console.log(1212)
}, error => {
    console.log(666, error);
})

// 建議使用
var p = new Promise((resolve, reject) => {
    throw new Error("test-error")
})
p.then(val=> {
    console.log(1212)
}).catch(error=> {
    console.log("error", error)
})


Promise.prototype.finally()

不管Promise最后狀態(tài)如何蒋院,都會執(zhí)行 finally() 亏钩。
finally() 方法的回調(diào)函數(shù)不接受任何參數(shù)。這也就意味著finally中無法知道 Promise 的狀態(tài)欺旧。這表明姑丑,finally里面的操作應(yīng)該和狀態(tài)無關(guān),不依賴與Promise 的執(zhí)行結(jié)果辞友。
finally的本質(zhì)是 then 方法的特例栅哀。
finally 方法不一定是最后一環(huán),后面還可以在跟 .then() 称龙,返回的是一個Promise

var p = new Promise((resolve, reject)=> {
    throw new Error("test~~")
})
p.then((val)=> {
    console.log(11, val)
}).finally(()=> {
    console.log('finally')
}).catch((err)=> {
    console.log(err)
})
//  finally
//  Error: test~~

還有

Promise.allSettled()

Promise.allSettled()方法接受一組 Promise 實(shí)例作為參數(shù)留拾,包裝成一個新的 Promise 實(shí)例。只有等到所有這些參數(shù)實(shí)例都返回結(jié)果鲫尊,不管是fulfilled還是rejected痴柔,包裝實(shí)例才會結(jié)束。

Promise.any()

ES2021 引入了Promise.any()方法疫向。該方法接受一組 Promise 實(shí)例作為參數(shù)咳蔚,包裝成一個新的 Promise 實(shí)例返回。只要參數(shù)實(shí)例有一個變成fulfilled狀態(tài)搔驼,包裝實(shí)例就會變成fulfilled狀態(tài)谈火;如果所有參數(shù)實(shí)例都變成rejected狀態(tài),包裝實(shí)例就會變成rejected狀態(tài)舌涨。
Promise.any()跟Promise.race()方法很像糯耍,只有一點(diǎn)不同,就是不會因?yàn)槟硞€ Promise 變成rejected狀態(tài)而結(jié)束囊嘉。

注意:
all(), race(), allSettled(), any() 都是 Promise 的方法温技;
then(), finally(), catch() 都是Promise實(shí)例的方法掛載在Promise.prototype上;

Promise和async/await(二)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末哗伯,一起剝皮案震驚了整個濱河市荒揣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌焊刹,老刑警劉巖长搀,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痒给,死亡現(xiàn)場離奇詭異,居然都是意外死亡徒爹,警方通過查閱死者的電腦和手機(jī)嘉蕾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進(jìn)店門贺奠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人错忱,你說我怎么就攤上這事儡率」揖荩” “怎么了?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵儿普,是天一觀的道長崎逃。 經(jīng)常有香客問我,道長眉孩,這世上最難降的妖魔是什么个绍? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮浪汪,結(jié)果婚禮上巴柿,老公的妹妹穿的比我還像新娘。我一直安慰自己死遭,他們只是感情好广恢,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著呀潭,像睡著了一般钉迷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蜗侈,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天篷牌,我揣著相機(jī)與錄音,去河邊找鬼踏幻。 笑死枷颊,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的该面。 我是一名探鬼主播夭苗,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼隔缀!你這毒婦竟也來了题造?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤猾瘸,失蹤者是張志新(化名)和其女友劉穎界赔,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體牵触,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡淮悼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了揽思。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袜腥。...
    茶點(diǎn)故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖钉汗,靈堂內(nèi)的尸體忽然破棺而出羹令,到底是詐尸還是另有隱情鲤屡,我是刑警寧澤,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布福侈,位于F島的核電站酒来,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏癌刽。R本人自食惡果不足惜役首,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望显拜。 院中可真熱鬧衡奥,春花似錦、人聲如沸远荠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽譬淳。三九已至档址,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間邻梆,已是汗流浹背守伸。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浦妄,地道東北人尼摹。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像剂娄,于是被迫代替她去往敵國和親蠢涝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評論 2 354