在實際開發中總會遇到許多異步的問題,最多見的場景即是接口請求以後必定要等一段時間才能獲得結果,若是遇到多個接口先後依賴,那麼問題就變得複雜。你們都一直在嘗試使用更好的方案來解決這些問題。最開始只能利用回調函數,後來開始有人使用Promise的思惟來搞定。到ES6中開始支持原生的Promise,引入Generator函數。javascript
直到ES7,有了async/await
。vue
這是一個用同步的思惟來解決異步問題的方案。java
我想不少人可能還不太分得清同步與異步的區別。若是你已經完全瞭解了事件循環,那麼想必對異步的概念應該很是瞭解。當咱們發出了請求,並不會等待響應結果,而是會繼續執行後面的代碼,響應結果的處理在以後的事件循環中解決。那麼同步的意思,就是等結果出來以後,代碼纔會繼續往下執行。react
咱們能夠用一個兩人問答的場景來比喻異步與同步。A向B問了一個問題以後,不等待B的回答,接着問下一個問題,這是異步。A向B問了一個問題以後,而後就笑呵呵的等着B回答,B回答了以後他纔會接着問下一個問題,這是同步。jquery
那麼咱們先記住這個特色,async/await
使用同步的思惟,來解決異步的問題。webpack
在繼續分析它的語法與使用以前,咱們先介紹一下如何在咱們的開發環境中支持該語法。程序員
若是你已經知道如何配置,可跳過web
async/await
語法這裏主要介紹兩種方式。vue-cli
首先在當前項目中使用npm下載babel-loader
。npm
> npm install babel-loader --save-dev
而後在配置文件webpack.confing.dev.js
中配置,在module.exports.module.rules
中添加以下配置元素便可。
{
test: /\.(js|jsx)$/, include: paths.appSrc, loader: require.resolve('babel-loader'), options: { cacheDirectory: true, }, },
若是你使用最新版本的create-react-app或者vue-cli來構建你的代碼,那麼它們應該已經支持了該配置。
首先安裝gulp插件
> npm install gulp-babel --save-dev
而後編寫任務
var gulp = require('gulp'); var babel = require('gulp-babel'); gulp.task('babel', function() { return gulp.src('src/app.js') .pipe(babel()) .pipe(gulp.dest('dist')); });
async函數是Generator的一個語法糖。若是你不知道Generator是什麼函數也沒有關係,咱們只須要知道async函數實際上返回的是一個Promise對象便可。
async function fn() { return 30; } // 或者 const fn = async () => { return 30; }
在聲明函數時,前面加上關鍵字async
,這就是async
的用法。當咱們用console.log
打印出上面聲明的函數fn,咱們能夠看到以下結果:
console.log(fn()); // result Promise = { __proto__: Promise, [[PromiseStatus]]: "resolved", [[PromiseValue]]: 30 }
很顯然,fn的運行結果其實就是一個Promise對象。所以咱們也可使用then來處理後續邏輯。
fn().then(res => {
console.log(res); // 30 })
await的含義爲等待。意思就是代碼須要等待await後面的函數運行完而且有了返回結果以後,才繼續執行下面的代碼。這正是同步的效果。
可是咱們須要注意的是,await關鍵字只能在async函數中使用。而且await後面的函數運行後必須返回一個Promise對象才能實現同步的效果。
當咱們使用一個變量去接收await的返回值時,該返回值爲Promise中resolve出來的值(也就是PromiseValue)。
1 // 定義一個返回Promise對象的函數 2 function fn() { 3 return new Promise((resolve, reject) => { 4 setTimeout(() => { 5 resolve(30); 6 }, 1000); 7 }) 8 } 9 10 // 而後利用async/await來完成代碼 11 const foo = async () => { 12 const t = await fn(); 13 console.log(t); 14 console.log('next code'); 15 } 16 17 foo(); 18 19 // result: 20 // 30 21 // next code
運行這個例子咱們能夠看出,當在async函數中,運行遇到await時,就會等待await後面的函數運行完畢,而不會直接執行next code
。
若是咱們直接使用then方法的話,想要達到一樣的結果,就不得不把後續的邏輯寫在then方法中。
const foo = () => { return fn().then(t => { console.log(t); console.log('next code'); }) } foo();
很顯然若是使用async/await的話,代碼結構會更加簡潔,邏輯也更加清晰。
在Promise中,咱們知道是經過catch的方式來捕獲異常。而當咱們使用async時,則經過try/catch
來捕獲異常。
1 function fn() { 2 return new Promise((resolve, reject) => { 3 setTimeout(() => { 4 reject('some error.'); 5 }, 1000); 6 }) 7 } 8 9 const foo = async () => { 10 try { 11 await fn(); 12 } catch (e) { 13 console.log(e); // some error 14 } 15 } 16 17 foo();
若是有多個await函數,那麼只會返回第一個捕獲到的異常。
1 function fn1() { 2 return new Promise((resolve, reject) => { 3 setTimeout(() => { 4 reject('some error fn1.'); 5 }, 1000); 6 }) 7 } 8 function fn2() { 9 return new Promise((resolve, reject) => { 10 setTimeout(() => { 11 reject('some error fn2.'); 12 }, 1000); 13 }) 14 } 15 16 const foo = async () => { 17 try { 18 await fn1(); 19 await fn2(); 20 } catch (e) { 21 console.log(e); // some error fn1. 22 } 23 } 24 25 foo();
在實踐中咱們遇到異步場景最多的就是接口請求,那麼這裏就以jquery中的$.get
爲例簡單展現一下如何配合async/await
來解決這個場景。
// 先定義接口請求的方法,因爲jquery封裝的幾個請求方法都是返回Promise實例,所以能夠直接使用await函數實現同步
1 const getUserInfo = () => $.get('xxxx/api/xx'); 2 3 const clickHandler = async () => { 4 try { 5 const resp = await getUserInfo(); 6 // resp爲接口返回內容,接下來利用它來處理對應的邏輯 7 console.log(resp); 8 9 // do something 10 } catch (e) { 11 // 處理錯誤邏輯 12 } 13 }
爲了保證邏輯的完整性,在實踐中
try/catch
必不可少。總之,不處理錯誤邏輯的程序員不是好程序員。
與Promise相比,我的認爲async/await
有必定的簡潔性,但也並不是就比Promise有絕對的優點,所以只能算是提供了另一種一樣很棒的方式,至於你們學習以後選擇哪一種方式來解決本身的問題,我認爲這僅僅只是我的的喜愛問題。