async/await 是一種特殊的語法,能夠更好的處理promise,可以讓你編寫基于Promise的代碼像同步一樣。
### async
async 關鍵字放在函數之前,使得該函數總是返回一個promise對象。如果代碼中顯式 return 了一個值,那么函數的返回值將會被自動包裝成resolved狀態下的promise對象。
例如:
~~~js
async function fn() {
return 1;
}
fn().then(res => {
console.log(res) // 1
})
~~~
##### async函數的使用方式
~~~js
// 函數聲明式
async function fn() { }
// 函數表達式
const fn = async function () { }
// ArrowFunc
const fn = async () => {}
// 對象中定義
let obj = {
async fn() {}
}
~~~
當async函數被調用執行的時候,會返回一個Promise的實例。當函數返回值時,promise會執行。如果函數在執行中拋出錯誤,那么會進入promise的rejected流程。
比如:
~~~js
const foo = async () => {
throw new Error('err');
}
foo()
.then(res => {
console.log(res);
})
.catch(err=> {
console.log(err) // Error: err
})
~~~
### await
await關鍵字只能在async函數中使用。可以用來等待Promise狀態變成resolved并有返回值。await后面通常跟的是一個promise對象,如果不是,會立即被包裝成resoled狀態的promise。
~~~js
async function foo() {
let result = await 'ok'
console.log(result); // ok
}
foo();
~~~
1. 當用變量接收await的返回值時,值為promise為resolved狀態下的值。
~~~js
const foo = async () => {
let result = await Promise.resolve('ok');
console.log(result); // ok
}
foo();
~~~
2. 函數執行中,遇到await,會首先返回promise,狀態為pending,并且下面的代碼會停止執行,待awiat后面的Promise執行完畢后才繼續執行下面的代碼,直到函數體中的代碼全部執行完畢后,函數返回的promise狀態才變成resolved。
~~~js
function bar() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok');
}, 1000);
})
}
async function foo() {
let result = await bar();
console.log(result); // ok
console.log('last');
}
console.log(foo());
~~~
輸出順序:

Markdown
首先打印出pending狀態的promise對象。
等到await后面的異步函數執行完后,才依次打印出ok、last,同時Promise的狀態成為resolved。

Markdown
由此可以看出,當在async函數中運行,當遇到await關鍵字時,會等待關鍵字后面的函數執行完畢才運行后面的代碼。當所有的執行完后函數返回的Promise對象的狀態才變成resolved。
### 異常處理
我們知道在promise中是通過catch來捕獲異常的。但是在async中則使用try/catch來捕獲異常。
1. 如果await后面的 promise 正常resolve,await promise便會返回結果。但是在reject的情況下,便會拋出異常,并且這種異常需要用try/catch來捕獲,否則會導致進程崩潰。
~~~js
const baz = () => {
return Promise.reject('Oops');
}
const foo = async () => {
try {
await baz();
} catch(e) {
// e 為 baz()返回的promise對象中 reject出來的值
console.log(e); // Oops
}
}
foo();
~~~
2. 如果try中有多個await,其中一個await后面的promise為reject,那么在catch中會拋出第一個異常。
如下:
~~~js
// 異步操作,返回promise對象
const bar = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('Oops bar error');
}, 1000)
})
}
const baz = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('Oops baz error');
}, 1000)
})
}
const foo = async () => {
try {
await bar();
await baz();
} catch(e) {
// e 為 bar()返回的promise對象中 reject出來的值
console.log(e); // Oops bar error
}
}
foo();
~~~
由此可以如果有多個 await 后面的promise都為reject狀態時,只能捕獲第一個異常。
3. 以下這種方式都會捕獲到。由于是同步的效果,所以第二次捕獲的異常是第一次捕獲1s后。
~~~js
const fn = async () => {
try {
await bar();
} catch (e) {
console.log(e) // Oops bar error
}
try {
await baz();
} catch (e) {
console.log(e) // Oops baz error
}
}
fn();
// 第二次和第一次的順序相隔1s
~~~
4. 如果await后面的promise中拋出異常,那么等同于async函數返回的 Promise 對象被reject。如下:
~~~js
async function foo() {
await new Promise((resolve, reject) => {
throw new Error('Oops');
});
}
foo()
.then(res => console.log('ok', res))
.catch(err => console.log('faile', err)) // faile Error: Oops
~~~
5. try里邊的promise中的異常不會在catch中捕獲,因為異常發生在promise中,只能通過promise的catch()捕獲。
~~~js
const foo = () => {
return new Promise((resolve, reject) => {
resolve('ok');
})
}
const bar = () => {
try {
foo()
.then(res => {
let data = JSON.parse(res);
}).catch(err => {
console.log(err); // SyntaxError: Unexpected token o in JSON at position 0
})
} catch(e) {
// 這里不會捕獲到promise中的異常
console.log(e);
}
}
bar();
~~~
#### 并行和串行
1. 串行
如果有多個await,那么會按照順序一個個的執行。
~~~js
const p1 = async () => {
return new Promise(resolve => {
setTimeout(() => {
resolve({ name: 'Rose' })
}, 1000);
})
}
const p2 = async () => {
return new Promise(resolve => {
setTimeout(() => {
resolve({ name: 'Rose' })
}, 1000);
})
}
const getInfo = async () => {
console.time('total time');
let p1Info = await p1();
let p2Info = await p2();
console.timeEnd('total time'); // total time: 2006.705810546875ms
}
getInfo()
~~~
因為每個await后面的異步函數都會延時1s,所以總耗時大于2s。
2. 并行
如果有多個await,那么先將await后面的異步函數的返回值保存變量中。
~~~js
const getInfo = async() => {
console.time('total time');
const [p1Info, p2Info] = await Promise.all([
p1(),
p2()
])
console.timeEnd('total time'); // total time: 1003.5810546875ms
}
getInfo()
~~~
Promise.all()返回值是一個新的promise對象,值為數組。
3. 循環中的串行
~~~js
const p = async (name) => {
return new Promise(resolve => {
let user = {name};
setTimeout(() => {
resolve(user)
}, 1000)
})
}
let arr = ['bob', 'mike'];
const getInfo = async() => {
console.time('total time');
for (let name of arr) {
const p3Info = await p(name);
// 每隔1s輸出一次
console.log(p3Info.name);
}
console.timeEnd('total time'); // total time: 2007.77783203125ms
}
~~~
輸出順序:
bob
mike
total time: 2007.77783203125ms
4. 循環中串行改并行
~~~js
const getInfo = async() => {
console.time('total time');
// 將所有的promise對象保存在數組中
let promises = arr.map(name => {
return p3(name);
})
for(let promise of promises) {
const Info = await promise;
console.log(Info.name);
}
console.timeEnd('total time'); // total time: 1006.291015625ms
}
~~~
### 總結:
#### async函數之前的關鍵字有兩個作用:
1. 使它總是返回一個promise。
2. 允許在其中使用await。
#### await promise之前的關鍵字使得JavaScript等待,直到這個promise的狀態為resolved
1. 如果是reject,則產生異常,需通過try/catch捕獲。
2. 否則,它返回結果,所以我們可以將它的值賦值給一個變量。
async/await使我們少寫promise.then/catch,但是不要忘記它們是基于promise的。
此外,網上很多關于promise的文章都會提到ajax的回調地獄,以此來說明promise的誕生只是用來解決異步的,其實不然。promise 只是解決異步的一種方式。
如果使用async/await,不僅代碼簡介,甚至有同步的feel。
promise是一種語法、一種形式,目前很多東西都是基于promise實現的,比如:jquery中的ajax,fetch,以及vue react中的很多功能也都使用了promise。所以promise是最最基本的。
0人點贊
[ECMAScript Next](/nb/16084136)
作者:一蕭煙雨任平生
鏈接:https://www.jianshu.com/p/fd3d571e38db
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
- 文檔說明
- 大廠面試題
- HTML
- 001.如何遍歷一個dom樹
- 002.為什么操作DOM會很慢
- 003.瀏覽器渲染HTML的步驟
- 004.DOM和JavaScript的關系
- JS
- 001.數組扁平化并去重排序
- 002.高階函數
- 003.sort() 對數組進行排序
- 004.call 、 apply 和bind的區別
- 006.0.1+0.2為什么等于0.30000000000000004
- 011.var、let、const 的區別及實現原理?
- 010.new操作符都做了什么
- 009.a.b.c.d 和 a['b']['c']['d'],哪個性能更高?
- 016.什么是防抖和節流?有什么區別?如何實現?
- 017.['1', '2', '3'].map(parseInt) what & why ?
- 018.為什么 for 循環嵌套順序會影響性能?
- 019.介紹模塊化發展歷程
- 020.push輸出問題
- 021.判斷數組的三個方法
- 022.全局作用域中,用 const 和 let 聲明的變量不在 window 上,那到底在哪里?如何去獲取?
- 023.輸出以下代碼的執行結果并解釋為什么
- 024.ES6 代碼轉成 ES5 代碼的實現思路是什么
- 025.為什么普通 for 循環的性能遠遠高于 forEach 的性能,請解釋其中的原因。
- 026.數組里面有10萬個數據,取第一個元素和第10萬個元素的時間相差多少
- 027.變量類型
- 028.原型和原型鏈
- 029.作用域和閉包
- 030. 異步
- 031.ES6/7 新標準的考查
- 024.事件冒泡/事件代理
- 025.手寫 XMLHttpRequest 不借助任何庫
- 026.什么是深拷貝?
- 0027.克隆數組的方法
- 0028.ES6之展開運算符(...)
- 0029.arguments
- 0030. requestAnimationFrame
- 0031.遞歸爆棧問題與解決
- 021.簡單改造下面的代碼,使之分別打印 10 和 20
- 032.箭頭函數與普通函數
- 033.去除掉html標簽字符串里的所有屬性
- 034.查找公共父節點
- 035.Promise
- 0036.JSON.stringify ()
- CSS
- 001. BFC
- 002.介紹下 BFC、IFC、GFC 和 FFC
- 003.分析比較 opacity: 0、visibility: hidden、display: none 優劣和適用場景
- 004.怎么讓一個 div 水平垂直居中
- 005.重排重繪
- 006.inline/block/inline-block的區別
- 007.選擇器的權重和優先級
- 008.盒模型
- 009.清除浮動
- 010.flex
- 011.nth-child和nth-of-type的區別
- 0012.overflow
- 0013.CSS3中translate、transform和translation的區別和聯系
- 0014.flex
- 0015.px、em、rem
- 0016.width:100%
- 網絡
- 001.講解下HTTPS的工作原理
- 002.介紹下 HTTPS 中間人攻擊
- 003.談談你對TCP三次握手和四次揮手的理解
- 004.A、B 機器正常連接后,B 機器突然重啟,問 A 此時處于 TCP 什么狀態
- 005.簡單講解一下http2的多路復用
- 006. 介紹下 http1.0、1.1、2.0 協議的區別?
- 007.永久性重定向(301)和臨時性重定向(302)對 SEO 有什么影響
- 008.URL從輸入到頁面展示的過程
- 009.接口如何防刷
- 010.http狀態碼?
- 0111.跨域/如何解決?
- 012.cookie 和 localStorage 有何區別?
- 013.Fetch API
- 014.跨域Ajax請求時是否帶Cookie的設置
- 0015.協商緩存和強緩存
- 性能優化
- 001.前后端分離的項目如何seo
- 002.性能優化的方法
- 003.防抖和節流
- React
- 001.React 中 setState 什么時候是同步的,什么時候是異步的?
- 002.Virtual DOM 真的比操作原生 DOM 快嗎?談談你的想法。
- 003.Hooks 的特別之處
- 004.元素和組件有什么區別?
- 005.什么是 Pure Components?
- 006.HTML 和 React 事件處理有什么區別?
- 007.如何將參數傳遞給事件處理程序或回調函數?
- 008.如何創建 refs?
- 009.什么是 forward refs?
- 010.什么是 Virtual DOM?
- 011.什么是受控組件、非受控組件?
- 012.什么是 Fragments ?
- 013.為什么React元素有一個$$typeof屬性?
- 014.如何在 React 中創建組件?
- 015.React 如何區分 Class 和 Function?
- 016.React 的狀態是什么?
- 017.React 中的 props 是什么?
- 018.狀態和屬性有什么區別?
- 019.如何在 JSX 回調中綁定方法或事件處理程序?
- 020.什么是 "key" 屬性,在元素數組中使用它們有什么好處?
- 021.為什么順序調用對 React Hooks 很重要?
- 022.setState如何知道該做什么?
- 023.hook規則?
- 024.Hooks 與 Class 中調用 setState 有不同的表現差異么?
- 025.useEffect
- 026.fiber的作用
- 027.context的作用?
- 028.setState何時同步何時異步?
- 029.react性能優化
- 030.fiber
- 031.React SSR
- 異步
- 001.介紹下promise
- 002.Async/Await 如何通過同步的方式實現異步
- 003.setTimeout、Promise、Async/Await 的區別
- 004.JS 異步解決方案的發展歷程以及優缺點
- 005.Promise 構造函數是同步執行還是異步執行,那么 then 方法呢?
- 006.模擬實現一個 Promise.finally
- 012.簡單手寫實現promise
- 015.用Promise對象實現的 Ajax
- 007.簡單實現async/await中的async函數
- 008.設計并實現 Promise.race()
- 009.Async/await
- 010.珠峰培訓promise
- git
- 001.提交但沒有push
- 002.gitignore沒有作用?
- Node
- 001.用nodejs,將base64轉化成png文件
- Koa
- 001.koa和express的區別
- 數據庫
- redux
- 001.redux 為什么要把 reducer 設計成純函數
- 002.在 React 中如何使用 Redux 的 connect() ?
- 003.mapStateToProps() 和 mapDispatchToProps() 之間有什么區別?
- 004.為什么 Redux 狀態函數稱為 reducers ?
- 005.如何在 Redux 中發起 AJAX 請求?
- 006.訪問 Redux Store 的正確方法是什么?
- 007.React Redux 中展示組件和容器組件之間的區別是什么?
- 008.Redux 中常量的用途是什么?
- 009.什么是 redux-saga?
- 設計模式
- 公司題目
- 001.餓了么
- 001.div垂直水平居中(flex、絕對定位)
- 002.React子父組件之間如何傳值
- 003.Emit事件怎么發,需要引入什么
- 004.介紹下React高階組件,和普通組件有什么區別
- 005.一個對象數組,每個子對象包含一個id和name,React如何渲染出全部的name
- 006.在哪個生命周期里寫
- 007.其中有幾個name不存在,通過異步接口獲取,如何做
- 008.渲染的時候key給什么值,可以使用index嗎,用id好還是index好
- 009.webpack如何配sass,需要配哪些loader
- 010.配css需要哪些loader
- 011.如何配置把js、css、html單獨打包成一個文件
- 012.監聽input的哪個事件,在什么時候觸發
- 013.兩個元素塊,一左一右,中間相距10像素
- 014.上下固定,中間滾動布局如何實現
- 016.取數組的最大值(ES5、ES6)
- 017.apply和call的區別
- 018.ES5和ES6有什么區別
- 019.some、every、find、filter、map、forEach有什么區別
- 020.上述數組隨機取數,每次返回的值都不一樣
- 021.如何找0-5的隨機數,95-99呢
- 022.頁面上有1萬個button如何綁定事件
- 023.如何判斷是button
- 024.頁面上生成一萬個button,并且綁定事件,如何做(JS原生操作DOM)
- 025.循環綁定時的index是多少,為什么,怎么解決
- 026.頁面上有一個input,還有一個p標簽,改變input后p標簽就跟著變化,如何處理
- 瀏覽器相關
- 001.性能優化
- 002.web安全
- 003.獲取瀏覽器大小
- 004.從輸入 URL 到頁面加載完成的過程中都發生了什么事情?
- 后端
- 001.分布式
- zuku
- 字節
- webpack
- webpack的打包原理是什么
- Webpack-- 常見面試題
- webscoket