【前端】從小白視角上手Promise、Async/Await和手撕代碼
寫在前面
對(duì)于前端新手而言,Promise是一件比較困擾學(xué)習(xí)的事情,需要理解的細(xì)節(jié)比較多。對(duì)于前端面試而言,Promise是面試官最常問的問題,特別是手撕源碼。眾所周知,JavaScript語言執(zhí)行環(huán)境是“單線程”。
單線程,就是指一次只能完成一件任務(wù),如果有多個(gè)任務(wù)就必須排隊(duì)等候,前面一個(gè)任務(wù)完成,再執(zhí)行后面一個(gè)任務(wù)。這種“單線程”模式執(zhí)行效率較低,任務(wù)耗時(shí)長(zhǎng)。
為了解決這個(gè)問題,就有了異步模式,也叫異步編程。
一、異步編程
所謂"異步",簡(jiǎn)單說就是一個(gè)任務(wù)分成兩段,先執(zhí)行第一段,然后轉(zhuǎn)而執(zhí)行其他任務(wù),當(dāng)?shù)谝欢斡辛藞?zhí)行結(jié)果之后,再回過頭執(zhí)行第二段。
JavaScript采用異步編程原因有兩點(diǎn):
- JavaScript是單線程。
 - 為了提高CPU的利用率。
 
在提高CPU的利用率的同時(shí)也提高了開發(fā)難度,尤其是在代碼的可讀性上。
那么異步存在的場(chǎng)景有:
- fs 文件操作
 
- require("fs").readFile("./index.html",(err,data)=>{})
 
- 數(shù)據(jù)庫(kù)操作
 - AJAX
 
- $.get("/user",(data)=>{})
 
定時(shí)器
- setTimeout(()=>{},2000)
 
二、Promise是什么
Promise理解
(1) 抽象表達(dá)
- Promise 是一門新的技術(shù)(es6規(guī)范)
 - Promise是js中進(jìn)行異步編程的新解決方案
 
(2) 具體表達(dá)
- 從語法上說:Promise是一個(gè)構(gòu)造函數(shù)
 - 從功能上說:Promise對(duì)象是用來封裝一個(gè)異步操作并可以獲取其成功/失敗的結(jié)果值
 
為什么要使用Promise
(1) 指定回調(diào)函數(shù)的方式更加靈活
- promise:?jiǎn)?dòng)異步任務(wù)=>返回promise對(duì)象=>給promise對(duì)象綁定回調(diào)函數(shù)
 
(2) 支持鏈?zhǔn)秸{(diào)用方式,可以解決回調(diào)地獄問題
- 什么是回調(diào)地獄?
 
回調(diào)地獄就是回調(diào)函數(shù)嵌套使用,外部回調(diào)函數(shù)異步執(zhí)行的結(jié)果是嵌套的回調(diào)執(zhí)行的條件
- 回調(diào)地獄的缺點(diǎn)
 
不便于閱讀
不便于異常處理
解決方法
Promise的狀態(tài)
- Promise必須擁有三種狀態(tài):pending、rejected、resolved
 - 如果Promise的狀態(tài)是pending時(shí),它可以變成成功fulfilled或失敗rejected
 - 如果promise是成功狀態(tài),則它不能轉(zhuǎn)換為任何狀態(tài),而且需要一個(gè)成功的值,并且這個(gè)值不能改變
 - 如果promise是失敗狀態(tài),則它不能轉(zhuǎn)換成任何狀態(tài),而且需要一個(gè)失敗的原因,并且這個(gè)值不能改變
 
Promise的狀態(tài)改變
pending未決定的,指的是實(shí)例狀態(tài)內(nèi)置的屬性
(1)pending變?yōu)閞esolved/fullfilled
(2)pending變?yōu)閞ejected
說明:Promise的狀態(tài)改變只有兩種,且一個(gè)Promise對(duì)象只能改變一次,無論失敗還是成功都會(huì)得到一個(gè)結(jié)果輸出,成功的結(jié)果一般是value,失敗的結(jié)果一般是reason。
無論狀態(tài)是成功還是失敗,返回的都是promise。
Promise的值
實(shí)例對(duì)象中的另一個(gè)屬性 [PromiseResult]保存著異步任務(wù) [成功/失敗] 的結(jié)果resolve/reject。
Promise的api
手寫Promide中的api:
(1)promise構(gòu)造函數(shù) Promise(executor){}
- executor:執(zhí)行器(resolve,reject)=>{}
 - resolve:內(nèi)部定義成功時(shí)我們需要調(diào)用的函數(shù)value=>{}
 - reject:內(nèi)部定義失敗時(shí)我們調(diào)用的函數(shù) reason=>{}說明:executor會(huì)在Promise內(nèi)部立即同步調(diào)用,異步操作在執(zhí)行器中執(zhí)行
 
(2)Promise.prototype.then方法:(onResolved,rejected)=>{}
- onResolved函數(shù):成功的回調(diào)函數(shù)value=>{}
 - rejected函數(shù):失敗的回調(diào)函數(shù)reason=>{}
 
說明:指定用于得到成功value的成功回調(diào)和用于得到失敗reason的失敗回調(diào),返回一個(gè)新的promise對(duì)象
(3)Promise.prototype.catch方法:(onRejected)=>{}
前三條是本文章中將要實(shí)現(xiàn)的手寫代碼,當(dāng)然Promise還有其它的api接口。
(1)Promise.prototype.finally()方法
finally()方法用于指定不管 Promise 對(duì)象最后狀態(tài)如何,都會(huì)執(zhí)行的操作。不管promise最后的狀態(tài),在執(zhí)行完then或catch指定的回調(diào)函數(shù)以后,都會(huì)執(zhí)行finally方法指定的回調(diào)函數(shù)。
- promise
 - .then(result => {···})
 - .catch(error => {···})
 - .finally(() => {···});
 
(2)Promise.all()方法
Promise.all()方法用于將多個(gè) Promise 實(shí)例,包裝成一個(gè)新的 Promise 實(shí)例。
- const p = Promise.all([p1, p2, p3]);
 
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ù)。
 
(3)Promise.race()方法
Promise.race()方法同樣是將多個(gè) Promise 實(shí)例,包裝成一個(gè)新的 Promise 實(shí)例。
- const p = Promise.race([p1, p2, p3]);
 
只要p1、p2、p3之中有一個(gè)實(shí)例率先改變狀態(tài),p的狀態(tài)就跟著改變。那個(gè)率先改變的 Promise 實(shí)例的返回值,就傳遞給p的回調(diào)函數(shù)。
(4)Promise.allSettled()方法
Promise.allSettled()方法接受一組 Promise 實(shí)例作為參數(shù),包裝成一個(gè)新的 Promise 實(shí)例。只有等到所有這些參數(shù)實(shí)例都返回結(jié)果,不管是fulfilled還是rejected,包裝實(shí)例才會(huì)結(jié)束。
- const promises = [
 - fetch('/api-1'),
 - fetch('/api-2'),
 - fetch('/api-3'),
 - ];
 - await Promise.allSettled(promises);
 - removeLoadingIndicator();
 
該方法返回的新的 Promise 實(shí)例,一旦結(jié)束,狀態(tài)總是fulfilled,不會(huì)變成rejected。狀態(tài)變成fulfilled后,Promise 的監(jiān)聽函數(shù)接收到的參數(shù)是一個(gè)數(shù)組,每個(gè)成員對(duì)應(yīng)一個(gè)傳入Promise.allSettled()的 Promise 實(shí)例。
(5)Promise.any()方法
ES2021 引入了Promise.any()方法。該方法接受一組 Promise 實(shí)例作為參數(shù),包裝成一個(gè)新的 Promise 實(shí)例返回。只要參數(shù)實(shí)例有一個(gè)變成fulfilled狀態(tài),包裝實(shí)例就會(huì)變成fulfilled狀態(tài);如果所有參數(shù)實(shí)例都變成rejected狀態(tài),包裝實(shí)例就會(huì)變成rejected狀態(tài)。
(6)Promise.reject(reason)方法
Promise.reject(reason)方法也會(huì)返回一個(gè)新的 Promise 實(shí)例,該實(shí)例的狀態(tài)為rejected。
- const p = Promise.reject('出錯(cuò)了');
 - // 等同于
 - const p = new Promise((resolve, reject) => reject('出錯(cuò)了'))
 - p.then(null, function (s) {
 - console.log(s)
 - });
 - // 出錯(cuò)了
 
(7)Promise.resolve()方法 有時(shí)需要將現(xiàn)有對(duì)象轉(zhuǎn)為 Promise 對(duì)象,Promise.resolve()方法就起到這個(gè)作用。
- Promise.resolve('foo')
 - // 等價(jià)于
 - new Promise(resolve => resolve('foo'))
 
改變promsie狀態(tài)和指定回調(diào)函數(shù)誰先誰后?
(1)都有可能,正常情況下是先指定回調(diào)函數(shù)再改變狀態(tài),但也可以先改變狀態(tài)再指定回調(diào)函數(shù)。
(2)如何改變狀態(tài)再指定回調(diào)?
- 在執(zhí)行器中直接調(diào)用resolve/reject
 - 延遲更長(zhǎng)時(shí)間才進(jìn)行調(diào)用then
 
(3)什么時(shí)候才能得到數(shù)據(jù)?
- 如果先指定的回調(diào),那當(dāng)狀態(tài)發(fā)生改變,回調(diào)函數(shù)就會(huì)調(diào)用,得到數(shù)據(jù)
 - 如果先改變的狀態(tài),那當(dāng)指定回調(diào)時(shí),回調(diào)函數(shù)就會(huì)進(jìn)行調(diào)用,得到數(shù)據(jù)
 
示例:
- let p = new Promise((resolve,reject)=>{
 - resolve("成功了");
 - reject("失敗了");
 - });
 - p.then((value)=>{
 - console.log(value);
 - },(reason)=>{
 - console.log(reason);
 - })
 
Promise規(guī)范
- "Promise"是一個(gè)具有then方法的對(duì)象或函數(shù),其行為符合此規(guī)范。也就是說Promise是一個(gè)對(duì)象或函數(shù)
 - "thenable"是一個(gè)具有then方法的對(duì)象或函數(shù),也就是這個(gè)對(duì)象必須擁有then方法
 - "value"是任何合法的js值(包括undefined或promise)
 - promise中的異常需要使用throw語句進(jìn)行拋出
 - promise失敗的時(shí)候需要給出失敗的原因
 
then方法說明
- 一個(gè)promise必須要有一個(gè)then方法,而且可以訪問promise最終的結(jié)果,成功或者失敗的值
 - then方法需要接收兩個(gè)參數(shù),onfulfilled和onrejected這兩個(gè)參數(shù)是可選參數(shù)
 - promise無論then方法是否執(zhí)行完畢,只要promise狀態(tài)變了,then中綁定的函數(shù)就會(huì)執(zhí)行。
 
鏈?zhǔn)秸{(diào)用
Promise最大的優(yōu)點(diǎn)就是可以進(jìn)行鏈?zhǔn)秸{(diào)用,如果一個(gè)then方法返回一個(gè)普通值,這個(gè)值就會(huì)傳遞給下一次的then中,作為成功的結(jié)果。
如果返回的是一個(gè)promise,則會(huì)把promise的執(zhí)行結(jié)果傳遞下去取決于這個(gè)promise的成功或失敗。
如果返回的是一個(gè)報(bào)錯(cuò),就會(huì)執(zhí)行到下一個(gè)then的失敗函數(shù)中。
三、手寫Promise代碼
面試經(jīng)??嫉氖謱慞romise代碼,可以仔細(xì)理解一下。
- // 手寫Promise
 - // 首先定義一個(gè)構(gòu)造函數(shù),在創(chuàng)建Promise對(duì)象的時(shí)候會(huì)傳遞一個(gè)函數(shù)executor,
 - // 這個(gè)函數(shù)會(huì)立即被調(diào)用,所以我們?cè)赑romise內(nèi)部立即執(zhí)行這個(gè)函數(shù)。
 - function Promise(executor){
 - // 用于保存promise的狀態(tài)
 - this.status = "pending";
 - this.value;//初始值
 - this.reason;//初始原因
 - this.onResolvedCallbacks = [];//存放所有成功的回調(diào)函數(shù)
 - this.onRejectedCallbacks = [];//存放所有失敗的回調(diào)函數(shù)
 - //定義resolve函數(shù)
 - const resolve = (value)=>{
 - if(this.status === "pending"){
 - this.status = "resolved";
 - this.value = value;
 - this.onResolvedCallbacks.forEach(function(fn){
 - fn()
 - })
 - }
 - }
 - //定義reject函數(shù)
 - const reject = (reason)=>{
 - if(this.status === "pending"){
 - this.status = "rejected";
 - this.reason = reason;
 - this.onRejectedCallbacks.forEach(function(fn){
 - fn()
 - })
 - }
 - }
 - executor(resolve,reject);
 - }
 - Promise.prototype.then = function(onFulfilled,onRejected){
 - /*
 - 每次then都會(huì)返回一個(gè)新的promise
 - 我們需要拿到當(dāng)前then方法執(zhí)行成功或失敗的結(jié)果,
 - 前一個(gè)then方法的返回值會(huì)傳遞給下一個(gè)then方法,
 - 所以這里我們要關(guān)心onFulfilled(self.value)
 - 和 onRejected(self.reason)的返回值,我們這里定義一個(gè)x來接收一下。
 - 如果失敗拋錯(cuò)需要執(zhí)行reject方法,這里使用try...catch捕獲一下錯(cuò)誤。
 - 也就是判斷then函數(shù)的執(zhí)行結(jié)果和返回的promise的關(guān)系。
 - */
 - return new Promise((resolve,reject)=>{
 - //當(dāng)Promise狀態(tài)為resolved時(shí)
 - if(this.status === "resolved"){
 - try{
 - resolve(onFulfilled(this.value))
 - }catch(error){
 - reject(error)
 - }
 - }
 - //當(dāng)Promise狀態(tài)為rejected時(shí)
 - if(this.status === "rejected"){
 - try {
 - resolve(onRejected(this.reason))
 - } catch (error) {
 - reject(error)
 - }
 - }
 - //當(dāng)Promise狀態(tài)為pendding
 - if(this.status === "pending"){
 - this.onResolvedCallbacks.push(function(){
 - try{
 - resolve(onFulfilled(this.value))
 - }catch(error){
 - reject(error)
 - }
 - });
 - this.onRejectedCallbacks.push(function(){
 - try {
 - resolve(onRejected(this.reason))
 - } catch (error) {
 - reject(error)
 - }
 - });
 - }
 - })
 - }
 
升級(jí)版Promise:
- class Promise{
 - /*首先定義一個(gè)構(gòu)造函數(shù),在創(chuàng)建Promise對(duì)象的時(shí)候會(huì)傳遞一個(gè)函數(shù)executor,
 - 這個(gè)函數(shù)會(huì)立即被調(diào)用,所以我們?cè)赑romise內(nèi)部立即執(zhí)行這個(gè)函數(shù)。*/
 - constructor(executor){
 - this.executor = executor(this.resolve,this.reject);
 - this.onResolvedCallbacks = [];//存放所有成功的回調(diào)函數(shù)
 - this.onRejectedCallbakcs = [];//存放所有失敗的回調(diào)函數(shù)
 - }
 - // 用于存儲(chǔ)相應(yīng)的狀態(tài)
 - status = "pending";
 - // 初始值
 - value;
 - // 初始原因
 - reason;
 - // executor在執(zhí)行的時(shí)候會(huì)傳入兩個(gè)方法,一個(gè)是resolve,
 - // 一個(gè)reject,所以我們要?jiǎng)?chuàng)建這兩個(gè)函數(shù),而且需要把這兩個(gè)函數(shù)傳遞給executor。
 - // 當(dāng)我們成功或者失敗的時(shí)候,執(zhí)行onFulfilled和onRejected的函數(shù),
 - // 也就是在resolve函數(shù)中和reject函數(shù)中分別循環(huán)執(zhí)行對(duì)應(yīng)的數(shù)組中的函數(shù)。
 - // 定義成功事件
 - resolve(value){
 - if(status === "pending"){
 - status = "resolved";
 - value = value;
 - this.onResolvedCallbacks.forEach(fn=>{fn()})
 - }
 - }
 - // 定義失敗事件
 - reject(){
 - if(this.status === "pending"){
 - this.status = "rejected";
 - this.reason = reason;
 - this.onRejectedCallbakcs.forEach(fn=>{fn()});
 - }
 - }
 - // 這個(gè)時(shí)候當(dāng)我們異步執(zhí)行resolve方法時(shí)候,then中綁定的函數(shù)就會(huì)執(zhí)行,并且綁定多個(gè)then的時(shí)候,多個(gè)方法都會(huì)執(zhí)行。
 - // Promise的對(duì)象存在一個(gè)then方法,這個(gè)then方法里面會(huì)有兩個(gè)參數(shù),一個(gè)是成功的回調(diào)onFulfilled,
 - // 另一個(gè)是失敗的回調(diào)onRejected,只要我們調(diào)用了resolve就會(huì)執(zhí)行onFulfilled,調(diào)用了reject就會(huì)執(zhí)行onRejected。
 - // 為了保證this不錯(cuò)亂,我們定義一個(gè)self存儲(chǔ)this。當(dāng)我們調(diào)用了resolve或reject的時(shí)候,需要讓狀態(tài)發(fā)生改變.
 - // 需要注意的是Promise的狀態(tài)只可改變一次,所以我們要判斷,只有當(dāng)狀態(tài)未發(fā)生改變時(shí),才去改變狀態(tài)。
 - then(onFulfilled,onRejected){
 - // 判斷當(dāng)前狀態(tài)進(jìn)行回調(diào)
 - if(this.status === "resolved"){
 - onFulfilled(self.value)
 - };
 - if(this.status === "rejected"){
 - onRejected(self.reason)
 - }
 - // 當(dāng)狀態(tài)還處于pending狀態(tài)時(shí)
 - // 因?yàn)閛nFulfilled和onRejected在執(zhí)行的時(shí)候需要傳入對(duì)應(yīng)的value值,所我們這里用一個(gè)函數(shù)包裹起來,將對(duì)應(yīng)的值也傳入進(jìn)去。
 - if(this.status === "pending"){
 - this.onResolvedCallbacks.push(()=>{onFulfilled(this.value)});
 - this.onResolvedCallbacks.push(()=>{onRejected(this.reason)});
 - }
 - }
 - }
 
使用自己手寫的Promise源碼:
- let p = new Promise((resolve,reject)=>{
 - setTimeout(()=>{
 - resolve("成功了")
 - },1000)
 - });
 - p.then(function(value){
 - return 123;
 - }).then(value=>{
 - console.log("收到了成功的消息:",value);
 - }).catch(error=>{
 - console.log(error);
 - });
 - p.then(value=>{
 - console.log(value);
 - })
 
四、Async/Await
async用來表示函數(shù)是異步的,定義的async函數(shù)返回值是一個(gè)promise對(duì)象,可以使用then方法添加回調(diào)函數(shù)。
await 可以理解為是 async wait 的簡(jiǎn)寫。await 必須出現(xiàn)在 async 函數(shù)內(nèi)部,不能單獨(dú)使用。函數(shù)中只要使用await,則當(dāng)前函數(shù)必須使用async修飾。
所以回調(diào)函數(shù)的終結(jié)者就是async/await。
async命令
- async函數(shù)返回的是一個(gè)promise對(duì)象。
 - async函數(shù)內(nèi)部return語句返回的值,會(huì)成為then方法回調(diào)的參數(shù)。
 - async函數(shù)內(nèi)部拋出錯(cuò)誤,會(huì)導(dǎo)致返回的 Promise 對(duì)象變?yōu)閞eject狀態(tài)。
 - 拋出的錯(cuò)誤對(duì)象會(huì)被catch方法回調(diào)函數(shù)接收到。
 
async函數(shù)返回的 Promise 對(duì)象,必須等到內(nèi)部所有await命令后面的 Promise 對(duì)象執(zhí)行完,才會(huì)發(fā)生狀態(tài)改變,除非遇到return語句或者拋出錯(cuò)誤。
也就是說,只有async函數(shù)內(nèi)部的異步操作執(zhí)行完,才會(huì)執(zhí)行then方法指定的回調(diào)函數(shù)。
- async function fun(){
 - // return "hello wenbo";
 - throw new Error("ERROR");
 - }
 - fun().then(v => console.log(v),reason=>console.log(reason));//Error: ERROR```
 
await命令
正常情況下,await命令后面是一個(gè) Promise 對(duì)象,返回該對(duì)象的結(jié)果。如果不是 Promise 對(duì)象,就直接返回對(duì)應(yīng)的值。
- async function fun(){
 - return await "zhaoshun";
 - // 等價(jià)于 return "zhaoshun";
 - }
 - fun().then(value=>console.log(value));//zhaoshun
 
另一種情況是,await命令后面是一個(gè)thenable對(duì)象(即定義了then方法的對(duì)象),那么await會(huì)將其等同于 Promise 對(duì)象。
- class Sleep{
 - constructor(timeout){
 - this.timeout = timeout;
 - }
 - then(resolve,reject){
 - const startTime = Date.now();
 - setTimeout(()=>resolve(Date.now() - startTime),this.timeout);
 - }
 - }
 - (
 - async ()=>{
 - const sleepTime = await new Sleep(1000);
 - console.log(sleepTime);//1012
 - }
 - )()
 - // js里面沒有休眠的語法,但是借助await命令可以讓程序停頓的時(shí)間
 - const sleepFun = (interval) => {
 - return new Promise(resolve=>{
 - setTimeout(resolve,interval);
 - })
 - }
 - // 用法
 - const asyncFun = async ()=>{
 - for(let i = 1; i <= 5; i++){
 - console.log(i);
 - await sleepFun(1000);
 - }
 - }
 - asyncFun();
 
從上面可以看到,await命令后面是一個(gè)Sleep對(duì)象的實(shí)例。這個(gè)實(shí)例不是 Promise 對(duì)象,但是因?yàn)槎x了then方法,await會(huì)將其視為Promise處理。
await命令后面的 Promise 對(duì)象如果變?yōu)閞eject狀態(tài),則reject的參數(shù)會(huì)被catch方法的回調(diào)函數(shù)接收到。
注意:上面代碼中,await語句前面沒有return,但是reject方法的參數(shù)依然傳入了catch方法的回調(diào)函數(shù)。這里如果在await前面加上return,效果是一樣的。
- 任何一個(gè)await語句后面的 Promise 對(duì)象變?yōu)閞eject狀態(tài),那么整個(gè)async函數(shù)都會(huì)中斷執(zhí)行。
 - 使用try/catch可以很好處理前面await中斷,而后面不執(zhí)行的情況。
 
示例:
- const fun = async ()=>{
 - try {
 - await Promise.reject("ERROR");
 - } catch (error) {
 - }
 - return await Promise.resolve("success");
 - }
 - fun().then(
 - value=>console.log(value),reason=>console.log(reason,"error")//
 - ).catch(
 - error=>console.log(error)//ERROR
 - );
 
另一種方法是await后面的 Promise 對(duì)象再跟一個(gè)catch方法,處理前面可能出現(xiàn)的錯(cuò)誤。
- const fun = async ()=>{
 - await Promise.reject("error").catch(e=>console.log(e));
 - return await Promise.resolve("success");
 - }
 - fun().then(v=>console.log(v));//success
 
錯(cuò)誤處理
第一點(diǎn):如果await后面的異步操作出錯(cuò),那么等同于async函數(shù)后面的promise對(duì)象被reject。
- const fun = async()=>{
 - await new Promise((resolve,reject)=>{
 - throw new Error("error")
 - })
 - }
 - fun().then(v=>console.log(v)).catch(e=>console.log(e));
 
第二點(diǎn):多個(gè)await命令后面的異步操作,如果不存在繼發(fā)關(guān)系,最好讓他們同時(shí)進(jìn)行觸發(fā)。
- const [fun1,fun2] = await Promise.all([getFun(),getFoo()]);
 - const fooPromise = getFoo();
 - const funPromise = getFun();
 - const fun1 = await fooPromise();
 - const fun2 = await funPromise();
 
第三點(diǎn):await命令只能用在async函數(shù)之中,如果用在普通函數(shù),就會(huì)報(bào)錯(cuò)。
- async function dbFuc(db) {
 - let docs = [{}, {}, {}];
 - // 報(bào)錯(cuò)
 - docs.forEach(function (doc) {
 - await db.post(doc);
 - });
 - }
 
第四點(diǎn):async 函數(shù)可以保留運(yùn)行堆棧。
小結(jié)在這篇文章中我們總結(jié)了異步編程和回調(diào)函數(shù)的解決方案Promise,以及回調(diào)終結(jié)者aysnc/await。
- Promise有三個(gè)狀態(tài):pending、rejected、resolved。
 - Promise的狀態(tài)改變,只能改變一次,只有兩種改變:pending變?yōu)閞esolved/fullfilled、pending變?yōu)閞ejected。
 - Promise最大優(yōu)點(diǎn)就是可以進(jìn)行鏈?zhǔn)秸{(diào)用。
 - async用來表示函數(shù)是異步的,定義的async函數(shù)返回值是一個(gè)promise對(duì)象。
 - 函數(shù)中只要使用await,則當(dāng)前函數(shù)必須使用async修飾。
 - 回調(diào)函數(shù)的終結(jié)者就是async/await。
 
參考文章
- 《異步終結(jié)者 async await,了解一下》
 - 《一次性讓你懂a(chǎn)sync/await,解決回調(diào)地獄》
 - 《面試精選之Promise》
 - 《BAT前端經(jīng)典面試問題:史上最最最詳細(xì)的手寫Promise教程》
 - 《Promise實(shí)現(xiàn)原理(附源碼)》
 - 《JavaScript高級(jí)程序設(shè)計(jì)(第四版)》
 - 《你不知道的JavaScript(中卷)》
 - 阮一峰《ES6 入門教程》
 
本文轉(zhuǎn)載自微信公眾號(hào)「前端萬有引力」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系前端萬有引力公眾號(hào)。
















 
 
 












 
 
 
 