由淺入深 promise 系列

前言

若是對(duì)執(zhí)行隊(duì)列,宏任務(wù)肺樟,微任務(wù)的不太理解的,建議先閱讀

這一次逻淌,徹底弄懂 JavaScript 執(zhí)行機(jī)制(別還不知道什么是宏任務(wù)么伯,什么是微任務(wù))

你盼世界,我盼望你無(wú)bug卡儒。Hello 大家好蹦狂!lesson

(聽說(shuō)封面和名字取得好就能把你騙進(jìn)來(lái) ??)

隱忍幾天為大家憋了個(gè)大招

這不,這一章節(jié)就是整理了45Promise的筆試題讓大家爽一爽 ??朋贬。

其實(shí)想要寫一篇關(guān)于Promise的文章是因?yàn)橹霸趯憚e的文章的時(shí)候被評(píng)論區(qū)的一名讀者無(wú)情的嘲諷了??:

"作者的Promise一定很爛"

所以編寫這么一個(gè)主題lesson我不是為了證明什么凯楔,而是想說(shuō):

"說(shuō)我爛我可以學(xué)啊"

image

另外查了很多關(guān)于Promise的面試題,有些一上來(lái)就很難的锦募,有些連著幾篇題目都是一樣的摆屯,還有一些比較好的文章介紹的都是一些硬知識(shí)點(diǎn)。

這篇文章是一篇比較純的Promise筆試文章,是我自己在做題的時(shí)候虐骑,根據(jù)題目想要的考點(diǎn)來(lái)反敲知識(shí)點(diǎn)准验,然后再由這個(gè)知識(shí)點(diǎn)編寫從淺到深的的題目。

所以你可以看到題目中有一些基礎(chǔ)題廷没,然后再?gòu)幕A(chǔ)題慢慢的變難糊饱,如果你看著感覺這段位配不上你的話,請(qǐng)答應(yīng)我堅(jiān)持看下去颠黎,會(huì)越來(lái)越難的...

咳咳另锋,循序漸進(jìn)嘛...

本文的題目沒有到特別深入,不過(guò)應(yīng)該覆蓋了大部分的考點(diǎn)狭归,另外為了不把大家繞混夭坪,答案也沒有考慮在Node的執(zhí)行結(jié)果,執(zhí)行結(jié)果全為瀏覽器環(huán)境下过椎。因此如果你都會(huì)做的話室梅,可以盡情的在評(píng)論區(qū)再給我一個(gè)??,放心疚宇,我脾氣很好的...

OK??亡鼠, 來(lái)看看通過(guò)閱讀本篇文章你可以學(xué)到:

  • Promise的幾道基礎(chǔ)題
  • Promise結(jié)合setTimeout
  • Promise中的then、catch敷待、finally
  • Promise中的all和race
  • async/await的幾道題
  • async處理錯(cuò)誤
  • 綜合題
  • 幾道大廠的面試題

前期準(zhǔn)備

在做下面??的題目之前拆宛,我希望你能清楚幾個(gè)知識(shí)點(diǎn)。

(如果你感覺一上來(lái)不想看這些列舉的知識(shí)點(diǎn)的話讼撒,直接看后面的例子再來(lái)理解它們也可以)

event loop它的執(zhí)行順序:

  • 一開始整個(gè)腳本作為一個(gè)宏任務(wù)執(zhí)行
  • 執(zhí)行過(guò)程中同步代碼直接執(zhí)行浑厚,宏任務(wù)進(jìn)入宏任務(wù)隊(duì)列,微任務(wù)進(jìn)入微任務(wù)隊(duì)列
  • 當(dāng)前宏任務(wù)執(zhí)行完出隊(duì)根盒,檢查微任務(wù)列表钳幅,有則依次執(zhí)行,直到全部執(zhí)行完
  • 執(zhí)行瀏覽器UI線程的渲染工作
  • 檢查是否有Web Worker任務(wù)炎滞,有則執(zhí)行
  • 執(zhí)行完本輪的宏任務(wù)敢艰,回到2,依此循環(huán)册赛,直到宏任務(wù)和微任務(wù)隊(duì)列都為空

微任務(wù)包括:MutationObserver钠导、Promise.then()或reject()Promise為基礎(chǔ)開發(fā)的其它技術(shù)森瘪,比如fetch API牡属、V8的垃圾回收過(guò)程、Node獨(dú)有的process.nextTick扼睬。

宏任務(wù)包括script逮栅、scriptsetTimeoutsetInterval 措伐、setImmediate 特纤、I/OUI rendering侥加。

注意??:在所有任務(wù)開始的時(shí)候捧存,由于宏任務(wù)中包括了script,所以瀏覽器會(huì)先執(zhí)行一個(gè)宏任務(wù)担败,在這個(gè)過(guò)程中你看到的延遲任務(wù)(例如setTimeout)將被放到下一輪宏任務(wù)中來(lái)執(zhí)行昔穴。

1. Promise的幾道基礎(chǔ)題

1.1 題目一

const promise1 = new Promise((resolve, reject) => {  
console.log('promise1'
)})
console.log('1', promise1);

過(guò)程分析:

  • 從上至下,先遇到new Promise氢架,執(zhí)行該構(gòu)造函數(shù)中的代碼promise1
  • 然后執(zhí)行同步代碼1傻咖,此時(shí)promise1沒有被resolve或者reject朋魔,因此狀態(tài)還是pending

結(jié)果:

'promise1''1' Promise{<pending>}

1.2 題目二

const promise = new Promise((resolve, reject) => {
  console.log(1);
  resolve('success')
  console.log(2);
});
promise.then(() => {
  console.log(3);
});
console.log(4);

過(guò)程分析:

  • 從上至下岖研,先遇到new Promise,執(zhí)行其中的同步代碼1
  • 再遇到resolve('success')警检, 將promise的狀態(tài)改為了resolved并且將值保存下來(lái)
  • 繼續(xù)執(zhí)行同步代碼2
  • 跳出promise孙援,往下執(zhí)行,碰到promise.then這個(gè)微任務(wù)扇雕,將其加入微任務(wù)隊(duì)列
  • 執(zhí)行同步代碼4
  • 本輪宏任務(wù)全部執(zhí)行完畢拓售,檢查微任務(wù)隊(duì)列,發(fā)現(xiàn)promise.then這個(gè)微任務(wù)且狀態(tài)為resolved镶奉,執(zhí)行它础淤。

結(jié)果:

1 2 4 3

1.3 題目三

const promise = new Promise((resolve, reject) => {  
  console.log(1);
  console.log(2);
});
promise.then(() => {  console.log(3);});console.log(4);

過(guò)程分析

  • 和題目二相似,只不過(guò)在promise中并沒有resolve或者reject
  • 因此promise.then并不會(huì)執(zhí)行哨苛,它只有在被改變了狀態(tài)之后才會(huì)執(zhí)行鸽凶。

結(jié)果:

1 2 4

1.4 題目四

const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
  resolve('resolve1')
})
const promise2 = promise1.then(res => {
  console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);

過(guò)程分析:

  • 從上至下,先遇到new Promise建峭,執(zhí)行該構(gòu)造函數(shù)中的代碼promise1
  • 碰到resolve函數(shù), 將promise1的狀態(tài)改變?yōu)?code>resolved, 并將結(jié)果保存下來(lái)
  • 碰到promise1.then這個(gè)微任務(wù)玻侥,將它放入微任務(wù)隊(duì)列
  • promise2是一個(gè)新的狀態(tài)為pendingPromise
  • 執(zhí)行同步代碼1, 同時(shí)打印出promise1的狀態(tài)是resolved
  • 執(zhí)行同步代碼2亿蒸,同時(shí)打印出promise2的狀態(tài)是pending
  • 宏任務(wù)執(zhí)行完畢凑兰,查找微任務(wù)隊(duì)列,發(fā)現(xiàn)promise1.then這個(gè)微任務(wù)且狀態(tài)為resolved边锁,執(zhí)行它姑食。

結(jié)果:

'promise1''1' Promise{<resolved>: 'resolve1'}'2' Promise{<pending>}'resolve1'

1.5 題目五

接下來(lái)看看這道題:

const fn = () => (new Promise((resolve, reject) => {
  console.log(1);
  resolve('success')
}))
fn().then(res => {
  console.log(res)
})
console.log('start')

這道題里最先執(zhí)行的是'start'嗎 ??? ?

請(qǐng)仔細(xì)看看哦茅坛,fn函數(shù)它是直接返回了一個(gè)new Promise的矢门,而且fn函數(shù)的調(diào)用是在start之前,所以它里面的內(nèi)容應(yīng)該會(huì)先執(zhí)行。

結(jié)果:

1'start''success'

1.6 題目六

如果把fn的調(diào)用放到start之后呢祟剔?

const fn = () =>
  new Promise((resolve, reject) => {
    console.log(1);
    resolve("success");
  });
console.log("start");
fn().then(res => {
  console.log(res);
});

是的隔躲,現(xiàn)在start就在1之前打印出來(lái)了,因?yàn)?code>fn函數(shù)是之后執(zhí)行的物延。

注意??:之前我們很容易就以為看到new Promise()就執(zhí)行它的第一個(gè)參數(shù)函數(shù)了宣旱,其實(shí)這是不對(duì)的,就像這兩道題中叛薯,我們得注意它是不是被包裹在函數(shù)當(dāng)中浑吟,如果是的話,只有在函數(shù)調(diào)用的時(shí)候才會(huì)執(zhí)行耗溜。

答案:

"start"1"success"

好嘞组力,學(xué)完了這幾道基礎(chǔ)題,讓我們來(lái)用個(gè)表情包壓壓驚抖拴。

image

2. Promise結(jié)合setTimeout

2.1 題目一

console.log('start')
setTimeout(() => {
  console.log('time')
})
Promise.resolve().then(() => {
  console.log('resolve')
})
console.log('end

過(guò)程分析:

  • 剛開始整個(gè)腳本作為一個(gè)宏任務(wù)來(lái)執(zhí)行燎字,對(duì)于同步代碼直接壓入執(zhí)行棧進(jìn)行執(zhí)行,因此先打印出startend阿宅。
  • setTimout作為一個(gè)宏任務(wù)被放入宏任務(wù)隊(duì)列(下一個(gè))
  • Promise.then作為一個(gè)微任務(wù)被放入微任務(wù)隊(duì)列
  • 本次宏任務(wù)執(zhí)行完候衍,檢查微任務(wù),發(fā)現(xiàn)Promise.then洒放,執(zhí)行它
  • 接下來(lái)進(jìn)入下一個(gè)宏任務(wù)蛉鹿,發(fā)現(xiàn)setTimeout,執(zhí)行往湿。

結(jié)果:

'start''end''resolve''time'

2.2 題目二

const promise = new Promise((resolve, reject) => {
  console.log(1);
  setTimeout(() => {
    console.log("timerStart");
    resolve("success");
    console.log("timerEnd");
  }, 0);
  console.log(2);
});
promise.then((res) => {
  console.log(res);
});
console.log(4);

過(guò)程分析:

和題目1.2很像妖异,不過(guò)在resolve的外層加了一層setTimeout定時(shí)器。

  • 從上至下领追,先遇到new Promise他膳,執(zhí)行該構(gòu)造函數(shù)中的代碼1
  • 然后碰到了定時(shí)器,將這個(gè)定時(shí)器中的函數(shù)放到下一個(gè)宏任務(wù)的延遲隊(duì)列中等待執(zhí)行
  • 執(zhí)行同步代碼2
  • 跳出promise函數(shù)蔓腐,遇到promise.then矩乐,但其狀態(tài)還是為pending,這里理解為先不執(zhí)行
  • 執(zhí)行同步代碼4
  • 一輪循環(huán)過(guò)后回论,進(jìn)入第二次宏任務(wù)散罕,發(fā)現(xiàn)延遲隊(duì)列中有setTimeout定時(shí)器,執(zhí)行它
  • 首先執(zhí)行timerStart傀蓉,然后遇到了resolve欧漱,將promise的狀態(tài)改為resolved且保存結(jié)果并將之前的promise.then推入微任務(wù)隊(duì)列
  • 繼續(xù)執(zhí)行同步代碼timerEnd
  • 宏任務(wù)全部執(zhí)行完畢,查找微任務(wù)隊(duì)列葬燎,發(fā)現(xiàn)promise.then這個(gè)微任務(wù)误甚,執(zhí)行它缚甩。

因此執(zhí)行結(jié)果為:

124"timerStart""timerEnd""success"

2.3 題目三

題目三分了兩個(gè)題目,因?yàn)榭粗疾畈欢嘁ぐ睿贿^(guò)執(zhí)行的結(jié)果卻不一樣擅威,大家不妨先猜猜下面兩個(gè)題目分別執(zhí)行什么:

(1):

setTimeout(() => {
  console.log('timer1');
  setTimeout(() => {
    console.log('timer3')
  }, 0)
}, 0)
setTimeout(() => {
  console.log('timer2')
}, 0)
console.log('start')

(2):

setTimeout(() => {
  console.log('timer1');
  Promise.resolve().then(() => {
    console.log('promise')
  })
}, 0)
setTimeout(() => {
  console.log('timer2')
}, 0)
console.log('start')

執(zhí)行結(jié)果:

'start''timer1''timer2''timer3'
'start''timer1''promise''timer2'

這兩個(gè)例子,看著好像只是把第一個(gè)定時(shí)器中的內(nèi)容換了一下而已冈钦。

一個(gè)是為定時(shí)器timer3郊丛,一個(gè)是為Promise.then

但是如果是定時(shí)器timer3的話,它會(huì)在timer2后執(zhí)行瞧筛,而Promise.then卻是在timer2之前執(zhí)行厉熟。

你可以這樣理解,Promise.then是微任務(wù)较幌,它會(huì)被加入到本輪中的微任務(wù)列表揍瑟,而定時(shí)器timer3是宏任務(wù),它會(huì)被加入到下一輪的宏任務(wù)中乍炉。

理解完這兩個(gè)案例绢片,可以來(lái)看看下面一道比較難的題目了。

2.3 題目三

Promise.resolve().then(() => {
  console.log('promise1');
  const timer2 = setTimeout(() => {
    console.log('timer2')
  }, 0)
});
const timer1 = setTimeout(() => {
  console.log('timer1')
  Promise.resolve().then(() => {
    console.log('promise2')
  })
}, 0)
console.log('start');

這道題稍微的難一些恩急,在promise中執(zhí)行定時(shí)器杉畜,又在定時(shí)器中執(zhí)行promise纪蜒;

并且要注意的是衷恭,這里的Promise是直接resolve的,而之前的new Promise不一樣纯续。

因此過(guò)程分析為:

  • 剛開始整個(gè)腳本作為第一次宏任務(wù)來(lái)執(zhí)行随珠,我們將它標(biāo)記為宏1,從上至下執(zhí)行
  • 遇到Promise.resolve().then這個(gè)微任務(wù)猬错,將then中的內(nèi)容加入第一次的微任務(wù)隊(duì)列標(biāo)記為微1
  • 遇到定時(shí)器timer1窗看,將它加入下一次宏任務(wù)的延遲列表,標(biāo)記為宏2倦炒,等待執(zhí)行(先不管里面是什么內(nèi)容)
  • 執(zhí)行宏1中的同步代碼start
  • 第一次宏任務(wù)(宏1)執(zhí)行完畢显沈,檢查第一次的微任務(wù)隊(duì)列(微1),發(fā)現(xiàn)有一個(gè)promise.then這個(gè)微任務(wù)需要執(zhí)行
  • 執(zhí)行打印出微1中同步代碼promise1逢唤,然后發(fā)現(xiàn)定時(shí)器timer2拉讯,將它加入宏2的后面,標(biāo)記為宏3
  • 第一次微任務(wù)隊(duì)列(微1)執(zhí)行完畢鳖藕,執(zhí)行第二次宏任務(wù)(宏2)魔慷,首先執(zhí)行同步代碼timer1
  • 然后遇到了promise2這個(gè)微任務(wù),將它加入此次循環(huán)的微任務(wù)隊(duì)列著恩,標(biāo)記為微2
  • 宏2中沒有同步代碼可執(zhí)行了院尔,查找本次循環(huán)的微任務(wù)隊(duì)列(微2)蜻展,發(fā)現(xiàn)了promise2,執(zhí)行它
  • 第二輪執(zhí)行完畢邀摆,執(zhí)行宏3纵顾,打印出timer2

所以結(jié)果為:

'start''promise1''timer1''promise2''timer2'

如果感覺有點(diǎn)繞的話,可以看下面這張圖栋盹,就一目了然了片挂。

image

2.4 題目四

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  }, 1000)
})
const promise2 = promise1.then(() => {
  throw new Error('error!!!')
})
console.log('promise1', promise1)
console.log('promise2', promise2)
setTimeout(() => {
  console.log('promise1', promise1)
  console.log('promise2', promise2)
}, 2000)

過(guò)程分析:

  • 從上至下,先執(zhí)行第一個(gè)new Promise中的函數(shù)贞盯,碰到setTimeout將它加入下一個(gè)宏任務(wù)列表
  • 跳出new Promise音念,碰到promise1.then這個(gè)微任務(wù),但其狀態(tài)還是為pending躏敢,這里理解為先不執(zhí)行
  • promise2是一個(gè)新的狀態(tài)為pendingPromise
  • 執(zhí)行同步代碼console.log('promise1')闷愤,且打印出的promise1的狀態(tài)為pending
  • 執(zhí)行同步代碼console.log('promise2'),且打印出的promise2的狀態(tài)為pending
  • 碰到第二個(gè)定時(shí)器件余,將其放入下一個(gè)宏任務(wù)列表
  • 第一輪宏任務(wù)執(zhí)行結(jié)束讥脐,并且沒有微任務(wù)需要執(zhí)行,因此執(zhí)行第二輪宏任務(wù)
  • 先執(zhí)行第一個(gè)定時(shí)器里的內(nèi)容啼器,將promise1的狀態(tài)改為resolved且保存結(jié)果并將之前的promise1.then推入微任務(wù)隊(duì)列
  • 該定時(shí)器中沒有其它的同步代碼可執(zhí)行旬渠,因此執(zhí)行本輪的微任務(wù)隊(duì)列,也就是promise1.then端壳,它拋出了一個(gè)錯(cuò)誤告丢,且將promise2的狀態(tài)設(shè)置為了rejected
  • 第一個(gè)定時(shí)器執(zhí)行完畢,開始執(zhí)行第二個(gè)定時(shí)器中的內(nèi)容
  • 打印出'promise1'损谦,且此時(shí)promise1的狀態(tài)為resolved
  • 打印出'promise2'岖免,且此時(shí)promise2的狀態(tài)為rejected

完整的結(jié)果為:

'promise1' Promise{<pending>}
'promise2' Promise{<pending>}
test5.html:102 Uncaught (in promise) Error: error!!! at test.html:102
'promise1' Promise{<resolved>: "success"}
'promise2' Promise{<rejected>: Error: error!!!}

2.5 題目五

如果你上面這道題搞懂了之后,我們就可以來(lái)做做這道了照捡,你應(yīng)該能很快就給出答案:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("success");
    console.log("timer1");
  }, 1000);
  console.log("promise1里的內(nèi)容");
});
const promise2 = promise1.then(() => {
  throw new Error("error!!!");
});
console.log("promise1", promise1);
console.log("promise2", promise2);
setTimeout(() => {
  console.log("timer2");
  console.log("promise1", promise1);
  console.log("promise2", promise2);
}, 2000);

結(jié)果:

'promise1里的內(nèi)容'
'promise1' Promise{<pending>}
'promise2' Promise{<pending>}
'timer1'
test5.html:102 Uncaught (in promise) Error: error!!! at test.html:102
'timer2'
'promise1' Promise{<resolved>: "success"}
'promise2' Promise{<rejected>: Error: error!!!}

3. Promise中的then颅湘、catch、finally

額栗精,可能你看到下面??這么多的1闯参,2,3脾氣就上來(lái)了悲立,不是說(shuō)好了本篇文章沒什么屁話嘛鹿寨,怎么還是這么多一二三四。

??级历,你要理解我的用心良苦啊释移,我這是幫你把知識(shí)點(diǎn)都列舉出來(lái),做個(gè)總結(jié)而已寥殖。當(dāng)然玩讳,你也可以先不看涩蜘,先去做后面的題,然后再回過(guò)頭來(lái)看這些熏纯,你就覺得這些點(diǎn)都好好懂啊同诫,甚至都不需要記。

總結(jié):

  1. Promise的狀態(tài)一經(jīng)改變就不能再改變樟澜。(見3.1)
  2. .then.catch都會(huì)返回一個(gè)新的Promise误窖。(上面的??1.4證明了)
  3. catch不管被連接到哪里,都能捕獲上層的錯(cuò)誤秩贰。(見3.2)
  4. Promise中霹俺,返回任意一個(gè)非 promise 的值都會(huì)被包裹成 promise 對(duì)象,例如return 2會(huì)被包裝為return Promise.resolve(2)毒费。
  5. Promise.then 或者 .catch 可以被調(diào)用多次, 當(dāng)如果Promise內(nèi)部的狀態(tài)一經(jīng)改變丙唧,并且有了一個(gè)值,那么后續(xù)每次調(diào)用.then或者.catch的時(shí)候都會(huì)直接拿到該值觅玻。(見3.5)
  6. .then 或者 .catchreturn 一個(gè) error 對(duì)象并不會(huì)拋出錯(cuò)誤想际,所以不會(huì)被后續(xù)的.catch 捕獲。(見3.6)
  7. .then.catch 返回的值不能是 promise 本身溪厘,否則會(huì)造成死循環(huán)胡本。(見3.7)
  8. .then 或者 .catch 的參數(shù)期望是函數(shù),傳入非函數(shù)則會(huì)發(fā)生值穿透畸悬。(見3.8)
  9. .then方法是能接收兩個(gè)參數(shù)的侧甫,第一個(gè)是處理成功的函數(shù),第二個(gè)是處理失敗的函數(shù)傻昙,再某些時(shí)候你可以認(rèn)為catch.then第二個(gè)參數(shù)的簡(jiǎn)便寫法闺骚。(見3.9)
  10. .finally方法也是返回一個(gè)Promise彩扔,他在Promise結(jié)束的時(shí)候妆档,無(wú)論結(jié)果為resolved還是rejected,都會(huì)執(zhí)行里面的回調(diào)函數(shù)虫碉。

3.1 題目一

const promise = new Promise((resolve, reject) => {
  resolve("success1");
  reject("error");
  resolve("success2");
});
promise
.then(res => {
    console.log("then: ", res);
  }).catch(err => {
    console.log("catch: ", err);
  })

構(gòu)造函數(shù)中的 resolvereject 只有第一次執(zhí)行有效贾惦,多次調(diào)用沒有任何作用 。驗(yàn)證了第一個(gè)結(jié)論敦捧,Promise的狀態(tài)一經(jīng)改變就不能再改變须板。

3.2 題目二

const promise = new Promise((resolve, reject) => {
  reject("error");
  resolve("success2");
});
promise
.then(res => {
    console.log("then: ", res);
  }).then(res => {
    console.log("then: ", res);
  }).catch(err => {
    console.log("catch: ", err);
  }).then(res => {
    console.log("then: ", res);
  })

結(jié)果:

"catch: " "error""then3: " undefined

驗(yàn)證了第三個(gè)結(jié)論,catch不管被連接到哪里兢卵,都能捕獲上層的錯(cuò)誤习瑰。

3.3 題目三

Promise.resolve(1)
  .then(res => {
    console.log(res);
    return 2;
  })
  .catch(err => {
    return 3;
  })
  .then(res => {
    console.log(res);
  });

結(jié)果:

12

Promise可以鏈?zhǔn)秸{(diào)用,不過(guò)promise 每次調(diào)用 .then 或者 .catch 都會(huì)返回一個(gè)新的 promise秽荤,從而實(shí)現(xiàn)了鏈?zhǔn)秸{(diào)用, 它并不像一般我們?nèi)蝿?wù)的鏈?zhǔn)秸{(diào)用一樣return this甜奄。

上面的輸出結(jié)果之所以依次打印出12,那是因?yàn)?code>resolve(1)之后走的是第一個(gè)then方法,并沒有走catch里监徘,所以第二個(gè)then中的res得到的實(shí)際上是第一個(gè)then的返回值藏古。

return 2會(huì)被包裝成resolve(2)

3.4 題目四

如果把3.3中的Promise.resolve(1)改為Promise.reject(1)又會(huì)怎么樣呢烟阐?

Promise.reject(1)
  .then(res => {
    console.log(res);
    return 2;
  })
  .catch(err => {
    console.log(err);
    return 3
  })
  .then(res => {
    console.log(res);
  });

結(jié)果:

13

結(jié)果打印的當(dāng)然是 1 和 3啦搬俊,因?yàn)?code>reject(1)此時(shí)走的就是catch,且第二個(gè)then中的res得到的就是catch中的返回值蜒茄。

3.5 題目五

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('timer')
    resolve('success')
  }, 1000)
})
const start = Date.now();
promise.then(res => {
  console.log(res, Date.now() - start)
})
promise.then(res => {
  console.log(res, Date.now() - start)
})

執(zhí)行結(jié)果:

'timer'success 1001success 1002

當(dāng)然唉擂,如果你足夠快的話,也可能兩個(gè)都是1001檀葛。

Promise.then 或者 .catch 可以被調(diào)用多次楔敌,但這里 Promise構(gòu)造函數(shù)只執(zhí)行一次∽ぷ唬或者說(shuō) promise 內(nèi)部狀態(tài)一經(jīng)改變卵凑,并且有了一個(gè)值,那么后續(xù)每次調(diào)用 .then 或者 .catch 都會(huì)直接拿到該值胜臊。

3.6 題目六

Promise.resolve().then(() => {
  return new Error('error!!!')
}).then(res => {
  console.log("then: ", res)
}).catch(err => {
  console.log("catch: ", err)
})

猜猜這里的結(jié)果輸出的是什么 ??? 勺卢?

你可能想到的是進(jìn)入.catch然后被捕獲了錯(cuò)誤。

結(jié)果并不是這樣的象对,它走的是.then里面:

"then: " "Error: error!!!"

這也驗(yàn)證了第4點(diǎn)和第6點(diǎn)黑忱,返回任意一個(gè)非 promise 的值都會(huì)被包裹成 promise 對(duì)象,因此這里的return new Error('error!!!')也被包裹成了return Promise.resolve(new Error('error!!!'))勒魔。

當(dāng)然如果你拋出一個(gè)錯(cuò)誤的話甫煞,可以用下面??兩的任意一種:

return Promise.reject(new Error('error!!!'));// orthrow new Error('error!!!')

3.7 題目七

const promise = Promise.resolve().then(() => {  return promise;})promise.catch(console.err)

.then.catch 返回的值不能是 promise 本身,否則會(huì)造成死循環(huán)冠绢。

因此結(jié)果會(huì)報(bào)錯(cuò):

Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

3.8 題目八

Promise.resolve(1)  .then(2)  .then(Promise.resolve(3))  .then(console.log)

這道題看著好像很簡(jiǎn)單抚吠,又感覺很復(fù)雜的樣子,怎么這么多個(gè).then啊... ??

image

其實(shí)你只要記住原則8.then 或者 .catch 的參數(shù)期望是函數(shù)弟胀,傳入非函數(shù)則會(huì)發(fā)生值穿透楷力。

第一個(gè)then和第二個(gè)then中傳入的都不是函數(shù),一個(gè)是數(shù)字類型孵户,一個(gè)是對(duì)象類型萧朝,因此發(fā)生了穿透,將resolve(1) 的值直接傳到最后一個(gè)then里夏哭。

所以輸出結(jié)果為:

1

3.9 題目九

下面來(lái)介紹一下.then函數(shù)中的兩個(gè)參數(shù)检柬。

第一個(gè)參數(shù)是用來(lái)處理Promise成功的函數(shù),第二個(gè)則是處理失敗的函數(shù)竖配。

也就是說(shuō)Promise.resolve('1')的值會(huì)進(jìn)入成功的函數(shù)何址,Promise.reject('2')的值會(huì)進(jìn)入失敗的函數(shù)酱固。

讓我們來(lái)看看這個(gè)例子??:

Promise.reject('err!!!')
  .then((res) => {
    console.log('success', res)
  }, (err) => {
    console.log('error', err)
  }).catch(err => {
    console.log('catch', err)
  })

這里的執(zhí)行結(jié)果是:

'error' 'error!!!'

它進(jìn)入的是then()中的第二個(gè)參數(shù)里面,而如果把第二個(gè)參數(shù)去掉头朱,就進(jìn)入了catch()中:

Promise.reject('err!!!')
  .then((res) => {
    console.log('success', res)
  }).catch(err => {
    console.log('catch', err)
  })

執(zhí)行結(jié)果:

'catch' 'error!!!'

但是有一個(gè)問(wèn)題运悲,如果是這個(gè)案例呢?

Promise.resolve()
  .then(function success (res) {
    throw new Error('error!!!')
  }, function fail1 (err) {
    console.log('fail1', err)
  }).catch(function fail2 (err) {
    console.log('fail2', err)
  })

由于Promise調(diào)用的是resolve()项钮,因此.then()執(zhí)行的應(yīng)該是success()函數(shù)班眯,可是success()函數(shù)拋出的是一個(gè)錯(cuò)誤,它會(huì)被后面的catch()給捕獲到烁巫,而不是被fail1函數(shù)捕獲署隘。

因此執(zhí)行結(jié)果為:

fail2 Error: error!!!           at success

3.10 題目十

接著來(lái)看看.finally(),這個(gè)功能一般不太用在面試中亚隙,不過(guò)如果碰到了你也應(yīng)該知道該如何處理磁餐。

其實(shí)你只要記住它三個(gè)很重要的知識(shí)點(diǎn)就可以了:

  1. .finally()方法不管Promise對(duì)象最后的狀態(tài)如何都會(huì)執(zhí)行
  2. .finally()方法的回調(diào)函數(shù)不接受任何的參數(shù),也就是說(shuō)你在.finally()函數(shù)中是沒法知道Promise最終的狀態(tài)是resolved還是rejected
  3. 它最終返回的默認(rèn)會(huì)是一個(gè)原來(lái)的Promise對(duì)象值阿弃,不過(guò)如果拋出的是一個(gè)異常則返回異常的Promise對(duì)象诊霹。

來(lái)看看這個(gè)簡(jiǎn)單的例子??:

Promise.resolve('1')
  .then(res => {
    console.log(res)
  })
  .finally(() => {
    console.log('finally')
  })
Promise.resolve('2')
  .finally(() => {
    console.log('finally2')
    return '我是finally2返回的值'
  })
  .then(res => {
    console.log('finally2后面的then函數(shù)', res)
  })

這兩個(gè)Promise.finally都會(huì)執(zhí)行,且就算finally2返回了新的值渣淳,它后面的then()函數(shù)接收到的結(jié)果卻還是'2'脾还,因此打印結(jié)果為:

'1''finally2''finally''finally2后面的then函數(shù)' '2'

至于為什么finally2的打印要在finally前面,請(qǐng)看下一個(gè)例子中的解析入愧。

不過(guò)在此之前讓我們?cè)賮?lái)確認(rèn)一下鄙漏,finally中要是拋出的是一個(gè)異常是怎樣的:

Promise.resolve('1')
  .finally(() => {
    console.log('finally1')
    throw new Error('我是finally中拋出的異常')
  })
  .then(res => {
    console.log('finally后面的then函數(shù)', res)
  })
  .catch(err => {
    console.log('捕獲錯(cuò)誤', err)
  })

執(zhí)行結(jié)果為:

'finally1''捕獲錯(cuò)誤' Error: 我是finally中拋出的異常

但是如果改為return new Error('我是finally中拋出的異常'),打印出來(lái)的就是'finally后面的then函數(shù) 1'

OK棺蛛,??怔蚌,讓我們來(lái)看一個(gè)比較難的例子??:

function promise1 () {
  let p = new Promise((resolve) => {
    console.log('promise1');
    resolve('1')
  })
  return p;
}
function promise2 () {
  return new Promise((resolve, reject) => {
    reject('error')
  })
}
promise1()
  .then(res => console.log(res))
  .catch(err => console.log(err))
  .finally(() => console.log('finally1'))

promise2()
  .then(res => console.log(res))
  .catch(err => console.log(err))
  .finally(() => console.log('finally2'))

執(zhí)行過(guò)程:

  • 首先定義了兩個(gè)函數(shù)promise1promise2,先不管接著往下看旁赊。
  • promise1函數(shù)先被調(diào)用了桦踊,然后執(zhí)行里面new Promise的同步代碼打印出promise1
  • 之后遇到了resolve(1),將p的狀態(tài)改為了resolved并將結(jié)果保存下來(lái)彤恶。
  • 此時(shí)promise1內(nèi)的函數(shù)內(nèi)容已經(jīng)執(zhí)行完了钞钙,跳出該函數(shù)
  • 碰到了promise1().then(),由于promise1的狀態(tài)已經(jīng)發(fā)生了改變且為resolved因此將promise1().then()這條微任務(wù)加入本輪的微任務(wù)列表(這是第一個(gè)微任務(wù))
  • 這時(shí)候要注意了声离,代碼并不會(huì)接著往鏈?zhǔn)秸{(diào)用的下面走,也就是不會(huì)先將.finally加入微任務(wù)列表瘫怜,那是因?yàn)?code>.then本身就是一個(gè)微任務(wù)术徊,它鏈?zhǔn)胶竺娴膬?nèi)容必須得等當(dāng)前這個(gè)微任務(wù)執(zhí)行完才會(huì)執(zhí)行,因此這里我們先不管.finally()
  • 再往下走碰到了promise2()函數(shù)鲸湃,其中返回的new Promise中并沒有同步代碼需要執(zhí)行赠涮,所以執(zhí)行reject('error')的時(shí)候?qū)?code>promise2函數(shù)中的Promise的狀態(tài)變?yōu)榱?code>rejected
  • 跳出promise2函數(shù)子寓,遇到了promise2().then(),將其加入當(dāng)前的微任務(wù)隊(duì)列(這是第二個(gè)微任務(wù))笋除,且鏈?zhǔn)秸{(diào)用后面的內(nèi)容得等該任務(wù)執(zhí)行完后才執(zhí)行斜友,和.then()一樣。
  • OK垃它, 本輪的宏任務(wù)全部執(zhí)行完了鲜屏,來(lái)看看微任務(wù)列表,存在promise1().then()国拇,執(zhí)行它洛史,打印出1,然后遇到了.finally()這個(gè)微任務(wù)將它加入微任務(wù)列表(這是第三個(gè)微任務(wù))等待執(zhí)行
  • 再執(zhí)行promise2().catch()打印出error酱吝,執(zhí)行完后將finally2加入微任務(wù)加入微任務(wù)列表(這是第四個(gè)微任務(wù))
  • OK也殖, 本輪又全部執(zhí)行完了,但是微任務(wù)列表還有兩個(gè)新的微任務(wù)沒有執(zhí)行完务热,因此依次執(zhí)行finally1finally2忆嗜。

結(jié)果:

'promise1''1''error''finally1''finally2'

在這道題中其實(shí)能擴(kuò)展的東西挺多的,之前沒有提到崎岂,那就是你可以理解為鏈?zhǔn)秸{(diào)用后面的內(nèi)容需要等前一個(gè)調(diào)用執(zhí)行完才會(huì)執(zhí)行霎褐。

就像是這里的finally()會(huì)等promise1().then()執(zhí)行完才會(huì)將finally()加入微任務(wù)隊(duì)列,其實(shí)如果這道題中你把finally()換成是then()也是這樣的:

function promise1 () {
  let p = new Promise((resolve) => {
    console.log('promise1');
    resolve('1')
  })
  return p;
}
function promise2 () {
  return new Promise((resolve, reject) => {
    reject('error')
  })
}
promise1()
  .then(res => console.log(res))
  .catch(err => console.log(err))
  .then(() => console.log('finally1'))

promise2()
  .then(res => console.log(res))
  .catch(err => console.log(err))
  .then(() => console.log('finally2'))

4. Promise中的all和race

在做下面??的題目之前该镣,讓我們先來(lái)了解一下Promise.all()Promise.race()的用法冻璃。

通俗來(lái)說(shuō),.all()的作用是接收一組異步任務(wù)损合,然后并行執(zhí)行異步任務(wù)省艳,并且在所有異步操作執(zhí)行完后才執(zhí)行回調(diào)。

.race()的作用也是接收一組異步任務(wù)嫁审,然后并行執(zhí)行異步任務(wù)跋炕,只保留取第一個(gè)執(zhí)行完成的異步操作的結(jié)果,其他的方法仍在執(zhí)行律适,不過(guò)執(zhí)行結(jié)果會(huì)被拋棄辐烂。

來(lái)看看題目一。

4.1 題目一

我們知道如果直接在腳本文件中定義一個(gè)Promise捂贿,它構(gòu)造函數(shù)的第一個(gè)參數(shù)是會(huì)立即執(zhí)行的纠修,就像這樣:

const p1 = new Promise(r => console.log('立即打印'))

控制臺(tái)中會(huì)立即打印出 “立即打印”。

因此為了控制它什么時(shí)候執(zhí)行厂僧,我們可以用一個(gè)函數(shù)包裹著它扣草,在需要它執(zhí)行的時(shí)候,調(diào)用這個(gè)函數(shù)就可以了:

function runP1 () { 
const p1 = new Promise(r => console.log('立即打印'))    
return p1
}
runP1() // 調(diào)用此函數(shù)時(shí)才執(zhí)行

OK ??, 讓我們回歸正題辰妙。

現(xiàn)在來(lái)構(gòu)建這么一個(gè)函數(shù):

function runAsync (x) {
    const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
    return p
}

該函數(shù)傳入一個(gè)值x鹰祸,然后間隔一秒后打印出這個(gè)x

如果我用.all()來(lái)執(zhí)行它會(huì)怎樣呢密浑?

function runAsync (x) {
    const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
    return p
}
Promise.all([runAsync(1), runAsync(2), runAsync(3)])
  .then(res => console.log(res))

先來(lái)想想此段代碼在瀏覽器中會(huì)如何執(zhí)行蛙婴?

沒錯(cuò),當(dāng)你打開頁(yè)面的時(shí)候尔破,在間隔一秒后街图,控制臺(tái)會(huì)同時(shí)打印出1, 2, 3,還有一個(gè)數(shù)組[1, 2, 3]呆瞻。

123[1, 2, 3]

所以你現(xiàn)在能理解這句話的意思了嗎:有了all台夺,你就可以并行執(zhí)行多個(gè)異步操作,并且在一個(gè)回調(diào)中處理所有的返回?cái)?shù)據(jù)痴脾。

.all()后面的.then()里的回調(diào)函數(shù)接收的就是所有異步操作的結(jié)果颤介。

而且這個(gè)結(jié)果中數(shù)組的順序和Promise.all()接收到的數(shù)組順序一致!T蘩怠滚朵!

??

有一個(gè)場(chǎng)景是很適合用這個(gè)的,一些游戲類的素材比較多的應(yīng)用前域,打開網(wǎng)頁(yè)時(shí)辕近,預(yù)先加載需要用到的各種資源如圖片、flash以及各種靜態(tài)文件匿垄。所有的都加載完后移宅,我們?cè)龠M(jìn)行頁(yè)面的初始化。

4.2 題目二

我新增了一個(gè)runReject函數(shù)椿疗,它用來(lái)在1000 * x秒后reject一個(gè)錯(cuò)誤漏峰。

同時(shí).catch()函數(shù)能夠捕獲到.all()里最先的那個(gè)異常,并且只執(zhí)行一次届榄。

想想這道題會(huì)怎樣執(zhí)行呢 ???浅乔?

function runAsync (x) {
  const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
  return p
}
function runReject (x) {
  const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x))
  return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
  .then(res => console.log(res))
  .catch(err => console.log(err))

不賣關(guān)子了 ??,讓我來(lái)公布答案:

13// 2s后輸出2Error: 2// 4s后輸出4

沒錯(cuò)铝条,就像我之前說(shuō)的靖苇,.catch是會(huì)捕獲最先的那個(gè)異常,在這道題目中最先的異常就是runReject(2)的結(jié)果班缰。

另外贤壁,如果一組異步操作中有一個(gè)異常都不會(huì)進(jìn)入.then()的第一個(gè)回調(diào)函數(shù)參數(shù)中。

注意鲁捏,為什么不說(shuō)是不進(jìn)入.then()中呢 ???芯砸?

哈哈萧芙,大家別忘了.then()方法的第二個(gè)參數(shù)也是可以捕獲錯(cuò)誤的:

Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
  .then(res => console.log(res),
  err => console.log(err))

4.3 題目三

接下來(lái)讓我們看看另一個(gè)有趣的方法.race给梅。

讓我看看你們的英語(yǔ)水平如何假丧?

快!一秒鐘告訴我race是什么意思动羽?

image

好吧...你們果然很強(qiáng)...

race包帚,比賽,賽跑的意思运吓。

所以使用.race()方法渴邦,它只會(huì)獲取最先執(zhí)行完成的那個(gè)結(jié)果,其它的異步任務(wù)雖然也會(huì)繼續(xù)進(jìn)行下去拘哨,不過(guò)race已經(jīng)不管那些任務(wù)的結(jié)果了谋梭。

來(lái),改造一下4.1這道題:

function runAsync (x) {
  const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
  return p
}
Promise.race([runAsync(1), runAsync(2), runAsync(3)])
  .then(res => console.log('result: ', res))
  .catch(err => console.log(err))

執(zhí)行結(jié)果為:

1
result:  1
2
3

??

這個(gè)race有什么用呢倦青?使用場(chǎng)景還是很多的瓮床,比如我們可以用race給某個(gè)異步請(qǐng)求設(shè)置超時(shí)時(shí)間,并且在超時(shí)后執(zhí)行相應(yīng)的操作

4.4 題目四

改造一下題目4.2

function runAsync(x) {
  const p = new Promise(r =>
    setTimeout(() => r(x, console.log(x)), 1000)
  );
  return p;
}
function runReject(x) {
  const p = new Promise((res, rej) =>
    setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x)
  );
  return p;
}
Promise.race([runReject(0), runAsync(1), runAsync(2), runAsync(3)])
  .then(res => console.log("result: ", res))
  .catch(err => console.log(err));

遇到錯(cuò)誤的話产镐,也是一樣的隘庄,在這道題中,runReject(0)最先執(zhí)行完癣亚,所以進(jìn)入了catch()中:

0
'Error: 0'
1
2
3

總結(jié)

好的丑掺,讓我們來(lái)總結(jié)一下.then().race()吧,??

  • Promise.all()的作用是接收一組異步任務(wù)述雾,然后并行執(zhí)行異步任務(wù)街州,并且在所有異步操作執(zhí)行完后才執(zhí)行回調(diào)。
  • .race()的作用也是接收一組異步任務(wù)玻孟,然后并行執(zhí)行異步任務(wù)唆缴,只保留取第一個(gè)執(zhí)行完成的異步操作的結(jié)果,其他的方法仍在執(zhí)行取募,不過(guò)執(zhí)行結(jié)果會(huì)被拋棄琐谤。
  • Promise.all().then()結(jié)果中數(shù)組的順序和Promise.all()接收到的數(shù)組順序一致。

5. async/await的幾道題

既然談到了Promise玩敏,那就肯定得再說(shuō)說(shuō)async/await斗忌,在很多時(shí)候asyncPromise的解法差不多,又有些不一樣旺聚。不信你來(lái)看看題目一织阳。

5.1 題目一

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  console.log("async2");
}
async1();
console.log('start')

這道基礎(chǔ)題輸出的是啥?

答案:

'async1 start''async2''start''async1 end'

過(guò)程分析:

  • 首先一進(jìn)來(lái)是創(chuàng)建了兩個(gè)函數(shù)的砰粹,我們先不看函數(shù)的創(chuàng)建位置唧躲,而是看它的調(diào)用位置
  • 發(fā)現(xiàn)async1函數(shù)被調(diào)用了,然后去看看調(diào)用的內(nèi)容
  • 執(zhí)行函數(shù)中的同步代碼async1 start,之后碰到了await弄痹,它會(huì)阻塞async1后面代碼的執(zhí)行饭入,因此會(huì)先去執(zhí)行async2中的同步代碼async2,然后跳出async1
  • 跳出async1函數(shù)后肛真,執(zhí)行同步代碼start
  • 在一輪宏任務(wù)全部執(zhí)行完之后谐丢,再來(lái)執(zhí)行剛剛await后面的內(nèi)容async1 end

(在這里蚓让,你可以理解為await后面的內(nèi)容就相當(dāng)于放到了Promise.then的里面)

來(lái)看看區(qū)別乾忱,如果我們把await async2()換成一個(gè)new Promise呢?

async function async1() {
  console.log("async1 start");
  new Promise(resolve => {
    console.log('promise')
  })
  console.log("async1 end");
}
async1();
console.log("start")

此時(shí)的執(zhí)行結(jié)果為:

'async start''promise''async1 end''start'

可以看到new Promise()并不會(huì)阻塞后面的同步代碼async1 end的執(zhí)行历极。

5.2 題目二

現(xiàn)在將async結(jié)合定時(shí)器看看窄瘟。

給題目一中的 async2函數(shù)中加上一個(gè)定時(shí)器:

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  setTimeout(() => {
    console.log('timer')
  }, 0)
  console.log("async2");
}
async1();
console.log("start")

沒錯(cuò),定時(shí)器始終還是最后執(zhí)行的趟卸,它被放到下一條宏任務(wù)的延遲隊(duì)列中蹄葱。

答案:

'async1 start''async2''start''async1 end''timer'

5.3 題目三

來(lái)吧,小伙伴們衰腌,讓我們多加幾個(gè)定時(shí)器看看新蟆。??

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
  setTimeout(() => {
    console.log('timer1')
  }, 0)
}
async function async2() {
  setTimeout(() => {
    console.log('timer2')
  }, 0)
  console.log("async2");
}
async1();
setTimeout(() => {
  console.log('timer3')
}, 0)
console.log("start")

思考一下??,執(zhí)行結(jié)果會(huì)是什么右蕊?

其實(shí)如果你能做到這里了琼稻,說(shuō)明你前面的那些知識(shí)點(diǎn)也都掌握了,我就不需要太過(guò)詳細(xì)的步驟分析了饶囚。

直接公布答案吧:

'async1 start''async2''start''async1 end''timer2''timer3''timer1'

定時(shí)器誰(shuí)先執(zhí)行帕翻,你只需要關(guān)注誰(shuí)先被調(diào)用的以及延遲時(shí)間是多少,這道題中延遲時(shí)間都是0萝风,所以只要關(guān)注誰(shuí)先被調(diào)用的嘀掸。。

5.4 題目四

正常情況下规惰,async中的await命令是一個(gè)Promise對(duì)象睬塌,返回該對(duì)象的結(jié)果。

但如果不是Promise對(duì)象的話歇万,就會(huì)直接返回對(duì)應(yīng)的值揩晴,相當(dāng)于Promise.resolve()

async function fn () {
  // return await 1234
  // 等同于
  return 123
}
fn().then(res => console.log(res))

結(jié)果:

123

5.5 題目五

async function async1 () {
  console.log('async1 start');
  await new Promise(resolve => {
    console.log('promise1')
  })
  console.log('async1 success');
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')

這道題目比較有意思,大家要注意了贪磺。

async1await后面的Promise是沒有返回值的硫兰,也就是它的狀態(tài)始終是pending狀態(tài),因此相當(dāng)于一直在await寒锚,await劫映,await卻始終沒有響應(yīng)...

image.gif

所以在await之后的內(nèi)容是不會(huì)執(zhí)行的违孝,也包括async1后面的 .then

答案為:

'script start''async1 start''promise1''script end'

5.6 題目六

讓我們給5.5中的Promise加上resolve

async function async1 () {
  console.log('async1 start');
  await new Promise(resolve => {
    console.log('promise1')
    resolve('promise1 resolve')
  }).then(res => console.log(res))
  console.log('async1 success');
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')

現(xiàn)在Promise有了返回值了泳赋,因此await后面的內(nèi)容將會(huì)被執(zhí)行:

'script start''async1 start''promise1''script end''promise1 resolve''async1 success''async1 end'

5.7 題目七

async function async1 () {
  console.log('async1 start');
  await new Promise(resolve => {
    console.log('promise1')
    resolve('promise resolve')
  })
  console.log('async1 success');
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res => {
  console.log(res)
})
new Promise(resolve => {
  console.log('promise2')
  setTimeout(() => {
    console.log('timer')
  })
})

這道題應(yīng)該也不難雌桑,不過(guò)有一點(diǎn)需要注意的,在async1中的new Promise它的resovle的值和async1().then()里的值是沒有關(guān)系的摹蘑,很多小伙伴可能看到resovle('promise resolve')就會(huì)誤以為是async1().then()中的返回值筹燕。

因此這里的執(zhí)行結(jié)果為:

'script start''async1 start''promise1''promise2''async1 success''sync1 end''timer'

5.8 題目八

我們?cè)賮?lái)看一道頭條曾經(jīng)的面試題:

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}

async function async2() {
  console.log("async2");
}

console.log("script start");

setTimeout(function() {
  console.log("setTimeout");
}, 0);

async1();

new Promise(function(resolve) {
  console.log("promise1");
  resolve();
}).then(function() {
  console.log("promise2");
});
console.log('script end')

有了上面??幾題做基礎(chǔ)轧飞,相信你很快也能答上來(lái)了衅鹿。

image

自信的寫下你們的答案吧。

'script start''async1 start''async2''promise1''script end''async1 end''promise2''setTimeout'

(這道題最后async1 endpromise2的順序其實(shí)在網(wǎng)上飽受爭(zhēng)議过咬,我這里使用瀏覽器Chrome V80大渤,Node v12.16.1的執(zhí)行結(jié)果都是上面這個(gè)答案)

5.9 題目九

好的??,async/await大法已練成掸绞,咱們繼續(xù):

async function testSometing() {
  console.log("執(zhí)行testSometing");
  return "testSometing";
}

async function testAsync() {
  console.log("執(zhí)行testAsync");
  return Promise.resolve("hello async");
}

async function test() {
  console.log("test start...");
  const v1 = await testSometing();
  console.log(v1);
  const v2 = await testAsync();
  console.log(v2);
  console.log(v1, v2);
}

test();

var promise = new Promise(resolve => {
  console.log("promise start...");
  resolve("promise");
});
promise.then(val => console.log(val));

console.log("test end...");

答案:

'test start...'
'執(zhí)行testSometing'
'promise start...'
'test end...'
'testSometing'
'執(zhí)行testAsync'
'promise'
'hello async'
'testSometing' 'hello async'

6. async處理錯(cuò)誤

6.1 題目一

async中泵三,如果 await后面的內(nèi)容是一個(gè)異常或者錯(cuò)誤的話衔掸,會(huì)怎樣呢烫幕?

async function async1 () {
  await async2();
  console.log('async1');
  return 'async1 success'
}
async function async2 () {
  return new Promise((resolve, reject) => {
    console.log('async2')
    reject('error')
  })
}
async1().then(res => console.log(res))

例如這道題中,await后面跟著的是一個(gè)狀態(tài)為rejectedpromise敞映。

如果在async函數(shù)中拋出了錯(cuò)誤较曼,則終止錯(cuò)誤結(jié)果,不會(huì)繼續(xù)向下執(zhí)行振愿。

所以答案為:

'async2'Uncaught (in promise) error

如果改為throw new Error也是一樣的:

async function async1 () {  
  console.log('async1');  
  throw new Error('error!!!') 
 return 'async1 success'}async1().then(res => console.log(res))

結(jié)果為:

'async1'Uncaught (in promise) Error: error!!!

6.2 題目二

如果想要使得錯(cuò)誤的地方不影響async函數(shù)后續(xù)的執(zhí)行的話捷犹,可以使用try catch

async function async1 () {
  try {
    await Promise.reject('error!!!')
  } catch(e) {
    console.log(e)
  }
  console.log('async1');
  return Promise.resolve('async1 success')
}
async1().then(res => console.log(res))
console.log('script start')

這里的結(jié)果為:

'script start''error!!!''async1''async1 success'

或者你可以直接在Promise.reject后面跟著一個(gè)catch()方法:

async function async1 () {
  // try {
  //   await Promise.reject('error!!!')
  // } catch(e) {
  //   console.log(e)
  // }
  await Promise.reject('error!!!')
    .catch(e => console.log(e))
  console.log('async1');
  return Promise.resolve('async1 success')
}
async1().then(res => console.log(res))
console.log('script start')

運(yùn)行結(jié)果是一樣的。

7. 綜合題

上面??的題目都是被我拆分著說(shuō)一些功能點(diǎn)冕末,現(xiàn)在讓我們來(lái)做一些比較難的綜合題吧萍歉。

7.1 題目一

const first = () => (new Promise((resolve, reject) => {
    console.log(3);
    let p = new Promise((resolve, reject) => {
        console.log(7);
        setTimeout(() => {
            console.log(5);
            resolve(6);
            console.log(p)
        }, 0)
        resolve(1);
    });
    resolve(2);
    p.then((arg) => {
        console.log(arg);
    });

}));

first().then((arg) => {
    console.log(arg);
});
console.log(4);

過(guò)程分析:

  • 第一段代碼定義的是一個(gè)函數(shù),所以我們得看看它是在哪執(zhí)行的档桃,發(fā)現(xiàn)它在4之前枪孩,所以可以來(lái)看看first函數(shù)里面的內(nèi)容了。(這一步有點(diǎn)類似于題目1.5)
  • 函數(shù)first返回的是一個(gè)new Promise()藻肄,因此先執(zhí)行里面的同步代碼3
  • 接著又遇到了一個(gè)new Promise()蔑舞,直接執(zhí)行里面的同步代碼7
  • 執(zhí)行完7之后,在p中仅炊,遇到了一個(gè)定時(shí)器斗幼,先將它放到下一個(gè)宏任務(wù)隊(duì)列里不管它,接著向下走
  • 碰到了resolve(1)抚垄,這里就把p的狀態(tài)改為了resolved蜕窿,且返回值為1谋逻,不過(guò)這里也先不執(zhí)行
  • 跳出p,碰到了resolve(2)桐经,這里的resolve(2)毁兆,表示的是把first函數(shù)返回的那個(gè)Promise的狀態(tài)改了,也先不管它阴挣。
  • 然后碰到了p.then气堕,將它加入本次循環(huán)的微任務(wù)列表,等待執(zhí)行
  • 跳出first函數(shù)畔咧,遇到了first().then()茎芭,將它加入本次循環(huán)的微任務(wù)列表(p.then的后面執(zhí)行)
  • 然后執(zhí)行同步代碼4
  • 本輪的同步代碼全部執(zhí)行完畢,查找微任務(wù)列表誓沸,發(fā)現(xiàn)p.thenfirst().then()梅桩,依次執(zhí)行,打印出1和2
  • 本輪任務(wù)執(zhí)行完畢了拜隧,發(fā)現(xiàn)還有一個(gè)定時(shí)器沒有跑完宿百,接著執(zhí)行這個(gè)定時(shí)器里的內(nèi)容,執(zhí)行同步代碼5
  • 然后又遇到了一個(gè)resolve(6)洪添,它是放在p里的垦页,但是p的狀態(tài)在之前已經(jīng)發(fā)生過(guò)改變了,因此這里就不會(huì)再改變干奢,也就是說(shuō)resolve(6)相當(dāng)于沒任何用處痊焊,因此打印出來(lái)的pPromise{<resolved>: 1}。(這一步類似于題目3.1)

結(jié)果:

374125Promise{<resolved>: 1}

做對(duì)了的小伙伴獎(jiǎng)勵(lì)自己一朵小(大)(嘴)(巴)吧律胀,??

image

7.2 題目二

const async1 = async () => {
  console.log('async1');
  setTimeout(() => {
    console.log('timer1')
  }, 2000)
  await new Promise(resolve => {
    console.log('promise1')
  })
  console.log('async1 end')
  return 'async1 success'
}
console.log('script start');
async1().then(res => console.log(res));
console.log('script end');
Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .catch(4)
  .then(res => console.log(res))
setTimeout(() => {
  console.log('timer2')
}, 1000)

注意的知識(shí)點(diǎn):

*   `async`函數(shù)中`await`的`new Promise`要是沒有返回值的話則不執(zhí)行后面的內(nèi)容(類似題`5.5`)
*   `.then`函數(shù)中的參數(shù)期待的是函數(shù)宋光,如果不是函數(shù)的話會(huì)發(fā)生穿透(類似題`3.8` )
*   注意定時(shí)器的延遲時(shí)間

因此本題答案為:

'script start''async1''promise1''script end'1'timer2''timer1'


#### 7.3 題目三

const p1 = new Promise((resolve) => {
setTimeout(() => {
resolve('resolve3');
console.log('timer1')
}, 0)
resolve('resovle1');
resolve('resolve2');
}).then(res => {
console.log(res)
setTimeout(() => {
console.log(p1)
}, 1000)
}).finally(res => {
console.log('finally', res)
})


注意的知識(shí)點(diǎn):

*   `Promise`的狀態(tài)一旦改變就無(wú)法改變(類似題目`3.5`)
*   `finally`不管`Promise`的狀態(tài)是`resolved`還是`rejected`都會(huì)執(zhí)行,且它的回調(diào)函數(shù)是沒有參數(shù)的(類似`3.10`)

答案:

'resolve1''finally' undefined'timer1'Promise{<resolved>: undefined}


### 8\. 幾道大廠的面試題

#### 8.1 使用Promise實(shí)現(xiàn)每隔1秒輸出1,2,3

這道題比較簡(jiǎn)單的一種做法是可以用`Promise`配合著`reduce`不停的在`promise`后面疊加`.then`炭菌,請(qǐng)看下面的代碼:

const arr = [1, 2, 3]
arr.reduce((p, x) => {
return p.then(() => {
return new Promise(r => {
setTimeout(() => r(console.log(x)), 1000)
})
})
}, Promise.resolve())


或者你可以更簡(jiǎn)單一點(diǎn)寫:

const arr = [1, 2, 3]
arr.reduce((p, x) =>
p.then(() =>
new Promise(r =>
setTimeout(() => r(console.log(x)), 1000))),
Promise.resolve())


參考鏈接:如何讓異步操作順序執(zhí)行

#### 8.2 使用Promise實(shí)現(xiàn)紅綠燈交替重復(fù)亮

紅燈3秒亮一次罪佳,黃燈2秒亮一次,綠燈1秒亮一次黑低;如何讓三個(gè)燈不斷交替重復(fù)亮燈赘艳?(用Promise實(shí)現(xiàn))三個(gè)亮燈函數(shù)已經(jīng)存在:

function red() {
console.log('red');}function green() {
console.log('green');}function yellow() {
console.log('yellow');
}


答案:

const time = (timer) => {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, timer)
})
}
const ajax1 = () => time(2000).then(() => {
console.log(1);
return 1
})
const ajax2 = () => time(1000).then(() => {
console.log(2);
return 2
})
const ajax3 = () => time(1000).then(() => {
console.log(3);
return 3
})

function mergePromise () {
// 在這里寫代碼
}

mergePromise([ajax1, ajax2, ajax3]).then(data => {
console.log("done");
console.log(data); // data 為 [1, 2, 3]
});

// 要求分別輸出
// 1
// 2
// 3
// done
// [1, 2, 3]


#### 8.3 實(shí)現(xiàn)mergePromise函數(shù)

實(shí)現(xiàn)mergePromise函數(shù),把傳進(jìn)去的數(shù)組按順序先后執(zhí)行克握,并且把返回的數(shù)據(jù)先后放到數(shù)組data中蕾管。

const time = (timer) => { return new Promise(resolve => { setTimeout(() => { resolve() }, timer) })}const ajax1 = () => time(2000).then(() => { console.log(1); return 1})const ajax2 = () => time(1000).then(() => { console.log(2); return 2})const ajax3 = () => time(1000).then(() => { console.log(3); return 3})function mergePromise () { // 在這里寫代碼}mergePromise([ajax1, ajax2, ajax3]).then(data => { console.log("done"); console.log(data); // data 為 [1, 2, 3]});// 要求分別輸出// 1// 2// 3// done// [1, 2, 3]


這道題有點(diǎn)類似于`Promise.all()`,不過(guò)`.all()`不需要管執(zhí)行順序菩暗,只需要并發(fā)執(zhí)行就行了掰曾。但是這里需要等上一個(gè)執(zhí)行完畢之后才能執(zhí)行下一個(gè)。

解題思路:

*   定義一個(gè)數(shù)組`data`用于保存所有異步操作的結(jié)果
*   初始化一個(gè)`const promise = Promise.resolve()`停团,然后循環(huán)遍歷數(shù)組旷坦,在`promise`后面添加執(zhí)行`ajax`任務(wù)掏熬,同時(shí)要將添加的結(jié)果重新賦值到`promise`上。

答案:

function mergePromise (ajaxArray) {
// 存放每個(gè)ajax的結(jié)果
const data = [];
let promise = Promise.resolve();
ajaxArray.forEach(ajax => {
// 第一次的then為了用來(lái)調(diào)用ajax
// 第二次的then是為了獲取ajax的結(jié)果
promise = promise.then(ajax).then(res => {
data.push(res);
return data; // 把每次的結(jié)果返回
})
})
// 最后得到的promise它的值就是data
return promise;
}


#### 8.4 根據(jù)promiseA+實(shí)現(xiàn)一個(gè)自己的promise

說(shuō)真的秒梅,這道題被問(wèn)到的概率還是挺高的旗芬,而且要說(shuō)的內(nèi)容也很多...

這里偷個(gè)懶,不想細(xì)說(shuō)了...

不過(guò)哈捆蜀,我保證疮丛,下下題我一定仔細(xì)說(shuō) ??.

![image](https://upload-images.jianshu.io/upload_images/21481816-720baa3632f60aea?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

來(lái)吧,給你們一些好的寶典:

*   《Promise不會(huì)辆它?誊薄?看這里!C渚暇屋!史上最通俗易懂的Promise!6蠢薄!》
*   《寫一個(gè)符合 Promises/A+ 規(guī)范并可配合 ES7 async/await 使用的 Promise》

#### 8.5 封裝一個(gè)異步加載圖片的方法

這個(gè)相對(duì)簡(jiǎn)單一些昙衅,只需要在圖片的`onload`函數(shù)中扬霜,使用`resolve`返回一下就可以了。

來(lái)看看具體代碼:

function loadImg(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function() {
console.log("一張圖片加載完成");
resolve(img);
};
img.onerror = function() {
reject(new Error('Could not load image at' + url));
};
img.src = url;
});


#### 8.6 限制異步操作的并發(fā)個(gè)數(shù)并盡可能快的完成全部

有8個(gè)圖片資源的url而涉,已經(jīng)存儲(chǔ)在數(shù)組`urls`中著瓶。

`urls`類似于`['https://image1.png', 'https://image2.png', ....]`

而且已經(jīng)有一個(gè)函數(shù)`function loadImg`,輸入一個(gè)`url`鏈接啼县,返回一個(gè)`Promise`材原,該`Promise`在圖片下載完成的時(shí)候`resolve`,下載失敗則`reject`季眷。

但有一個(gè)要求余蟹,任何時(shí)刻同時(shí)下載的鏈接**數(shù)量不可以超過(guò)3個(gè)**。

請(qǐng)寫一段代碼實(shí)現(xiàn)這個(gè)需求子刮,要求**盡可能快速**地將所有圖片下載完成威酒。

var urls = [
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting1.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting2.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting3.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting4.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting5.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn6.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn7.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn8.png",
];
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function() {
console.log("一張圖片加載完成");
resolve(img);
};
img.onerror = function() {
reject(new Error('Could not load image at' + url));
};
img.src = url;
});


看到這道題時(shí),我最開始的想法是:

*   拿到`urls`挺峡,然后將這個(gè)數(shù)組每3個(gè)`url`一組創(chuàng)建成一個(gè)二維數(shù)組
*   然后用`Promise.all()`每次加載一組`url`(也就是并發(fā)3個(gè))葵孤,這一組加載完再加載下一組。

這個(gè)想法從技術(shù)上說(shuō)并不難實(shí)現(xiàn)橱赠,有點(diǎn)類似于第三題弦疮。不過(guò)缺點(diǎn)也明顯恕刘,那就是每次都要等到上一組全部加載完之后,才加載下一組篮奄,那如果上一組有`2`個(gè)已經(jīng)加載完了,還有`1`個(gè)特別慢钙畔,還在加載,要等這個(gè)慢的也加載完才能進(jìn)入下一組。這明顯會(huì)照撑郏卡頓,影響加載效率纺裁。

但是開始沒有考慮這么多诫肠,因此有了第一個(gè)版本。

**如果你有興趣可以看看想法一的代碼欺缘,雖然對(duì)你沒什么幫助栋豫,想直接知道比較好的做法的小伙伴請(qǐng)?zhí)较敕ǘ?*

![image](https://upload-images.jianshu.io/upload_images/21481816-52df0c2aaae06396?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

**想法一**??:

function limitLoad (urls, handler, limit) {
const data = []; // 存儲(chǔ)所有的加載結(jié)果
let p = Promise.resolve();
const handleUrls = (urls) => { // 這個(gè)函數(shù)是為了生成3個(gè)url為一組的二維數(shù)組
const doubleDim = [];
const len = Math.ceil(urls.length / limit); // Math.ceil(8 / 3) = 3
console.log(len) // 3, 表示二維數(shù)組的長(zhǎng)度為3
for (let i = 0; i < len; i++) {
doubleDim.push(urls.slice(i * limit, (i + 1) * limit))
}
return doubleDim;
}
const ajaxImage = (urlCollect) => { // 將一組字符串url 轉(zhuǎn)換為一個(gè)加載圖片的數(shù)組
console.log(urlCollect)
return urlCollect.map(url => handler(url))
}
const doubleDim = handleUrls(urls); // 得到3個(gè)url為一組的二維數(shù)組
doubleDim.forEach(urlCollect => {
p = p.then(() => Promise.all(ajaxImage(urlCollect))).then(res => {
data.push(...res); // 將每次的結(jié)果展開,并存儲(chǔ)到data中 (res為:[img, img, img])
return data;
})
})
return p;
}
limitLoad(urls, loadImg, 3).then(res => {
console.log(res); // 最終得到的是長(zhǎng)度為8的img數(shù)組: [img, img, img, ...]
res.forEach(img => {
document.body.appendChild(img);
})
});


**想法二**??:

參考LHH大翰仔仔-Promise面試題

既然題目的要求是保證每次并發(fā)請(qǐng)求的數(shù)量為3谚殊,那么我們可以先請(qǐng)求`urls`中的前面三個(gè)(下標(biāo)為`0,1,2`)丧鸯,并且請(qǐng)求的時(shí)候使用`Promise.race()`來(lái)同時(shí)請(qǐng)求,三個(gè)中有一個(gè)先完成了(例如下標(biāo)為`1`的圖片)嫩絮,我們就把這個(gè)當(dāng)前數(shù)組中已經(jīng)完成的那一項(xiàng)(第`1`項(xiàng))換成還沒有請(qǐng)求的那一項(xiàng)(`urls`中下標(biāo)為`3`)丛肢。

直到`urls`已經(jīng)遍歷完了,然后將最后三個(gè)沒有完成的請(qǐng)求(也就是狀態(tài)沒有改變的`Promise`)用`Promise.all()`來(lái)加載它們剿干。

不多說(shuō)蜂怎,流程圖都給你畫好了,你可以結(jié)合流程圖再來(lái)看代碼置尔。

![image.gif](https://upload-images.jianshu.io/upload_images/21481816-0f892d29d095cf0d.gif?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

為了方便你查看杠步,我截了個(gè)圖,不過(guò)代碼在后面也有

(說(shuō)真的榜轿,要我看這一大長(zhǎng)串代碼我也不愿意...)

![image](https://upload-images.jianshu.io/upload_images/21481816-0edceea1e8b08e5f?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

代碼:

```jacascript
function limitLoad(urls, handler, limit) {
  let sequence = [].concat(urls); // 復(fù)制urls
  // 這一步是為了初始化 promises 這個(gè)"容器"
  let promises = sequence.splice(0, limit).map((url, index) => {
    return handler(url).then(() => {
      // 返回下標(biāo)是為了知道數(shù)組中是哪一項(xiàng)最先完成
      return index;
    });
  });
  // 注意這里要將整個(gè)變量過(guò)程返回幽歼,這樣得到的就是一個(gè)Promise,可以在外面鏈?zhǔn)秸{(diào)用
  return sequence
    .reduce((pCollect, url) => {
      return pCollect
        .then(() => {
          return Promise.race(promises); // 返回已經(jīng)完成的下標(biāo)
        })
        .then(fastestIndex => { // 獲取到已經(jīng)完成的下標(biāo)
            // 將"容器"內(nèi)已經(jīng)完成的那一項(xiàng)替換
          promises[fastestIndex] = handler(url).then(
            () => {
              return fastestIndex; // 要繼續(xù)將這個(gè)下標(biāo)返回谬盐,以便下一次變量
            }
          );
        })
        .catch(err => {
          console.error(err);
        });
    }, Promise.resolve()) // 初始化傳入
    .then(() => { // 最后三個(gè)用.all來(lái)調(diào)用
      return Promise.all(promises);
    });
}
limitLoad(urls, loadImg, 3)
  .then(res => {
    console.log("圖片全部加載完畢");
    console.log(res);
  })
  .catch(err => {
    console.error(err);
  });

后語(yǔ)

知識(shí)無(wú)價(jià)甸私,支持原創(chuàng)。

參考文章:

  • 《ES6之Promise常見面試題》
  • 《如何讓異步操作順序執(zhí)行》
  • 《大白話講解Promise(一)
  • 《LHH大翰仔仔-Promise面試題》
  • 《今日頭條async/await面試題執(zhí)行順序》
  • 《(2.4w字,建議收藏)??原生JS靈魂之問(wèn)(下), 沖刺??進(jìn)階最后一公里(附個(gè)人成長(zhǎng)經(jīng)驗(yàn)分享)》

你盼世界设褐, 我盼望你無(wú)bug颠蕴。這篇文章就介紹到這里,一口氣刷完了45道題助析,真的很爽有沒有...

image

反正我做到后面是越來(lái)越有勁犀被,也越來(lái)越自信了(有點(diǎn)飄,收一下...)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末外冀,一起剝皮案震驚了整個(gè)濱河市寡键,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌雪隧,老刑警劉巖西轩,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件员舵,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡藕畔,警方通過(guò)查閱死者的電腦和手機(jī)马僻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)注服,“玉大人韭邓,你說(shuō)我怎么就攤上這事∪艿埽” “怎么了女淑?”我有些...
    開封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)辜御。 經(jīng)常有香客問(wèn)我鸭你,道長(zhǎng),這世上最難降的妖魔是什么擒权? 我笑而不...
    開封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任袱巨,我火速辦了婚禮,結(jié)果婚禮上菜拓,老公的妹妹穿的比我還像新娘瓣窄。我一直安慰自己,他們只是感情好纳鼎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著裳凸,像睡著了一般贱鄙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上姨谷,一...
    開封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天逗宁,我揣著相機(jī)與錄音,去河邊找鬼梦湘。 笑死瞎颗,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的捌议。 我是一名探鬼主播哼拔,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼瓣颅!你這毒婦竟也來(lái)了倦逐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤宫补,失蹤者是張志新(化名)和其女友劉穎檬姥,沒想到半個(gè)月后曾我,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡健民,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年抒巢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秉犹。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蛉谜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凤优,到底是詐尸還是另有隱情悦陋,我是刑警寧澤,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布筑辨,位于F島的核電站俺驶,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏棍辕。R本人自食惡果不足惜暮现,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望楚昭。 院中可真熱鬧栖袋,春花似錦、人聲如沸抚太。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)尿贫。三九已至电媳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間庆亡,已是汗流浹背匾乓。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留又谋,地道東北人拼缝。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像彰亥,于是被迫代替她去往敵國(guó)和親咧七。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

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

  • 原文地址https://fancierpj0.github.io/iPromise/ 目錄 (づ ̄ 3 ̄)づ=> ...
    Cirs_冷崢子閱讀 1,374評(píng)論 3 6
  • 弄懂js異步 講異步之前剩愧,我們必須掌握一個(gè)基礎(chǔ)知識(shí)-event-loop猪叙。 我們知道JavaScript的一大特點(diǎn)...
    DCbryant閱讀 2,710評(píng)論 0 5
  • 前言 本文旨在簡(jiǎn)單講解一下javascript中的Promise對(duì)象的概念,特性與簡(jiǎn)單的使用方法。并在文末會(huì)附上一...
    _暮雨清秋_閱讀 2,197評(píng)論 0 3
  • 姚麗冰 學(xué)號(hào):16050120089 原文鏈接 zhuanlan.zhihu.com 【嵌牛導(dǎo)讀】Promise...
    小犽閱讀 478評(píng)論 0 0
  • 早晨穴翩,我從夢(mèng)中醒來(lái)犬第,喊醒我的是我的機(jī)器人保姆,新一天已經(jīng)開始了芒帕。我起床洗臉?biāo)⒀狼干ぃl(wèi)生間跟三十年前比起來(lái)發(fā)生了...
    6202adf230c4閱讀 168評(píng)論 0 0