1. 程式人生 > >async/await使用詳解

async/await使用詳解

Promise 的方式雖然解決了 callback hell,但是這種方式充滿了 Promise的 then() 方法,如果處理流程複雜的話,整段程式碼將充滿 then,程式碼流程不能很好的表示執行流程。

為什麼是async/await

在 es6 中,我們可以使用 Generator 函式控制流程,如下面這段程式碼:

  1. function* foo(x){

  2.    yield x +1;

  3.    yield x +2;

  4. return x +3;

  5. }

我們可以根據不斷地呼叫 Generator 物件的 next()方法來控制函式的流程。但是這樣彷彿不是那麼的語義化。因此,在 ES6 中封裝了 Generator 函式的語法糖 async 函式,但是將其定義在了 es7 中。ES7 定義出的 async

 函式,終於讓 JavaScript 對於非同步操作有了終極解決方案。 Async 函式是 Generator 函式的語法糖。使用 關鍵字 Async 來表示,在函式內部使用 await 來表示非同步。相較於 Generator,Async 函式的改進在於下面幾點:Generator 函式的執行必須依靠執行器,而 Async() 函式自帶執行器,呼叫方式跟普通函式的呼叫一樣。 Async 和 await 相較於 * 和 yield 更加語義化。 async 函式返回值是 Promise 物件,比 Generator 函式返回的 Iterator 物件方便,可以直接使用 then()方法進行呼叫。

那麼,我們通過一段小小的程式碼來說明 async/await 函式的用法:

未使用 async/await 的定時函式:

  1. fn =()=>{

  2. returnnewPromise((resolve, reject)=>{

  3.    setTimeout(()=>{

  4.      resolve(1)

  5. },2000)

  6. })

  7. }

  8. constFn=()=>{

  9.  fn().then((res)=>{

  10.    console.log(res)

  11. })

  12.  console.log(2)

  13. }

  14. Fn()

我相信能看到這裡的各位程式設計師大佬應該都知道這段程式碼的輸出狀況:先列印 2,2s 之後打印出 1。

使用 async/await 的定時函式:

  1. fn =()=>{

  2. returnnewPromise((resolve, reject)=>{

  3.    setTimeout(()=>{

  4.      resolve(1)

  5. },2000)

  6. })

  7. }

  8. constFn= async ()=>{

  9.  await fn().then((res)=>{

  10.    console.log(res)

  11. })

  12. }

  13. Fn()

  14. console.log(2)

這一段函式的輸出狀況是:2s 後列印 1,然後列印 2。

那麼,why?

  我們在字面上理解這兩個單詞 async 和 await:async 的意思是非同步,async 用於定義一個非同步函式,該函式返回一個 Promise。;await 的意思是等待,Promise 是一個承諾,await 也是一個承諾。Promise 的承諾是將返回值輸出到 then 的回掉函式裡面,無論是成功還是失敗。await 的承諾是無論颳風還是下雨,我都會等你完成在做其他的步驟。因此,在上面的運用了 async/await 的程式碼中,會等待 fn 完全執行完成並且非同步的回撥完成對返回值的處理之後在開始進行下一步操作的。其原理是將非同步函式轉變為同步操作。

實際運用

在上週的工作中,我在一段基於 node 完成的爬蟲操作中多次運用 async/await 來控制程式的執行流程:

  1. //虛擬碼

  2. let axiosArr =[];

  3. for(let i =0, len = arr.length; i < len; i++){

  4.  let params = qs.stringify({

  5. 'param': arr[i].index,

  6. })

  7.  axiosArr.push(axios.post(url, params,{

  8.    headers

  9. }))

  10. }

  11. /*

  12. *上面的迴圈是迴圈抓取2345條資料,平均每個資料要訪問16個介面

  13. *用axios.all同時詢問,當返回結束後將返回值處理

  14. *然後將返回值儲存到mongodb資料庫中

  15. */

  16. await axios.all(axiosArr).then(

  17.  axios.spread(function(){

  18. for(let i =0, len = arguments.length; i < len; i++){

  19.      let str =`${unescape(arguments[i].data.replace(/\\u/g,'%u'))}`;

  20.      str = basics.subStr(basics.deletN(basics.deletS(basics.cutStr(str))));

  21.      concentArr[i].concent = str

  22. }

  23.    mg.mongodbMain({

  24.      name: obj.name,

  25.      alias: obj.alias,

  26.      type: type,

  27.      url: obj.url,

  28.      drugsConcent: concentArr

  29. })

  30. }))

其實操作就這麼點,大家看一下程式碼都會懂。但是問題是,當我不使用 async/await 時,會產生的情況是會先訪問 2000 + 個數據,不斷訪問其 16 個介面,但是由於 promise 的 then 的回撥函式為非同步的,會掛起,而不是直接將資料存到資料庫中。這貌似和我們預想的不一樣啊。因此,我在這裡使用了 async/await 函式,使用同步處理非同步操作,將 promise 同步化,當 axios.all 訪問完成這每一條資料的 16 個介面後,直接將資料儲存到資料庫中,然後才會走到迴圈的下一層,依舊是訪問下一條資料的 16 個介面。

async/await 的身後事

我們說過了 async 函式返回值是 Promise 物件。

  1. const delay = timeout =>newPromise(resolve=> setTimeout(resolve, timeout));

  2. async function f(){

  3.    await delay(1000);

  4.    await delay(2000);

  5.    await delay(3000);

  6. return'done';

  7. }

  8. f().then(v => console.log(v));// 6s之後列印'done'

那麼其內部一旦丟擲異常,則會導致返回的 Promise 物件狀態變為 reject 狀態。丟擲的錯誤而會被 catch 方法回撥函式接收到。

  1. async function e(){

  2. thrownewError('error');

  3. }

  4. e().then(v => console.log(v))

  5. .catch( e => console.log(e));//丟擲的錯誤會被catch捕捉到

並且,async 有一個和 promise.all 相似的特性,就是內部一點有一個 await 函式報錯,後續的就不再執行了

  1. let fn1 =()=>{

  2. returnnewPromise((resolve,reject)=>{

  3.        setTimeout(()=>{

  4.            reject('故意丟擲錯誤');

  5. },500);

  6. });

  7. }

  8. let fn2 =()=>{

  9. returnnewPromise((resolve,reject)=>{

  10.        setTimeout(()=>{

  11.            resolve(1);

  12. },500);

  13. });

  14. }

  15. let getList = async ()=>{

  16.    let a = await fn1();

  17.    let b = await fn2();

  18. return{first: a,second:b};

  19. }

  20. getList().then(result=>{

  21.    console.log(result);

  22. }).catch(err=>{

  23.    console.log(err);// 由於fn1的報錯,async的狀態直接變成了rejected

  24. });

當 Promise 出現的時候,我們彷彿看到了回撥地獄的滅亡。當 Async/Await 出現時,非同步終於不是一件困難的事情。

相關推薦

C# 非同步程式設計async/await

非同步程式設計async/await詳解 1.關鍵字async 當函式使用async標記後,返回值必須為void,Task,Task<T>,當返回值為Task<T>時,函式內部只需要返回T型別,編譯器會自動包裝成Task<T>型別,如下

async await

ocs tar mozilla settime fine pro doc tin urn async await本身就是promise + generator的語法糖。 本文主要講述以下內容 async await 主要特性 async awiat 實質和

C#多線程和異步——Task和async/await

推廣 pre 問題 rect nco start 成了 logs too 閱讀目錄 一、什麽是異步 二、Task介紹 1 Task創建和運行 2 Task的阻塞方法(Wait/WaitAll/WaitAny) 3 Task的延續操作(WhenAny/W

Spring中@Async用法及簡單例項

Spring中@Async用法 引言: 在Java應用中,絕大多數情況下都是通過同步的方式來實現互動處理的;但是在處理與第三方系統互動的時候,容易造成響應遲緩的情況,之前大部分都是使用多執行緒來完成此類任務,其實,在spring 3.x之後,就已經內建了@Async來完美解決這個問題,本文將完成

ES6中的promise、asyncawait用法

res color spa tle turn 同步方法 set 調用 順序輸出 <!DOCTYPE html> <html> <head> <title>Promise、async、await</title&g

async/await使用深入

async和await作為非同步模型程式碼編寫的語法糖已經提供了一段時間不過一直沒怎麼用,由於最近需要在BeetleX webapi中整合對Task方法的支援,所以對async和await有了深入的瞭解和實踐應用.在這總結一下async和await的使用,主要涉及到:自定義Awaitable,在傳統非同步方法

現代JS中的流程控制:Callbacks 、Promises 、Async/Await

JavaScript經常聲稱是_非同步_。那是什麼意思?它如何影響發展?近年來這種方法有何變化? 請思考以下程式碼: result1 = doSomething1(); result2 = doSomething2(result1); 大多數語言都處理每一行同步。第一行執行並返回結果。第二行在第一行完

async/await使用

Promise 的方式雖然解決了 callback hell,但是這種方式充滿了 Promise的 then() 方法,如果處理流程複雜的話,整段程式碼將充滿 then,程式碼流程不能很好的表示執行流程。為什麼是async/await在 es6 中,我們可以使用 Genera

JS中的async/await的執行順序

雖然大家知道async/await,但是很多人對這個方法中內部怎麼執行的還不是很瞭解,本文是我看了一遍技術部落格理解 JavaScript 的 async/await(如果對async/await不熟悉可以先看下http://es6.ruanyifeng.com/#docs/async)後拓展了一下,我理了一

koa2第一天 async

一、什麼是async    async其實是ES7的才有的關鍵字,放在這裡說,其實是和我們前面所說的Promise,Generator有很大關聯的。async的意思是"非同步",顧名思義是有關非同步操作有關的關鍵字。下面我們就來構造一個async方法。 async function

Condition的await和signal原理(Condition下的生產消費模型)

Condition的await和signal與Object的wait與notify區別: 任何一個Java物件都天然繼承於Object類,線上程間實現通訊會用到Object的幾個方法,如wait(),wait(long timeout),wait(long timeout,int nan

es6常用的新特性 let,const,class,模版字串,構賦值,箭頭函式,for-of, keys values entries, export import, async await

let const class var命令和function命令宣告的全域性變數,依舊是頂層物件的屬性; 另一方面規定,let命令、const命令、class命令宣告的全域性變數,不屬於頂層物件的屬性。 也就是說,從ES6開始,全域性變數將逐步與頂層物件的屬性脫

從event loop到async await來了事件迴圈機制

JS為什麼是單執行緒的? 最初設計JS是用來在瀏覽器驗證表單操控DOM元素的是一門指令碼語言,如果js是多執行緒的那麼兩個執行緒同時對一個DOM元素進行了相互衝突的操作,那麼瀏覽器的解析器是無法執行的。 JS為什麼需要非同步? 如果JS中不存在非同步,只能自上而下執行,如果上一行解析時間很長,那麼下面的

「微信小程式」PHP非同步程序async-helper例項

PHP非同步程序async-helper例項詳解 PHP 的非同步程序助手,藉助於 AMQP 實現非同步執行 PHP 的方法,將一些很耗時、追求高可用、需要重試機制的操作放到非同步程序中去執行,將你的 HTTP 服務從繁重的業務邏輯中解脫出來。以一個較低的成本將傳統 PHP 業務邏輯轉換成非阻塞、高可用、可

nodejs Async

為了適應非同步程式設計,減少回撥的巢狀,我嘗試了很多庫。最終覺得還是async最靠譜。 Async的內容分為三部分: 流程控制:簡化十種常見流程的處理集合處理:如何使用非同步操作處理集合中的資料工具類:幾個常用的工具類 本文介紹其中最簡單最常用的流程控制部分。 由於node

python3.5+ asyncio await異步

asyncio tasks default eal 分享 lis cond task python3.5 import asyncio,time from collections import defaultdict from pprint import pprint

SpringBoot中@Async註解配合@EnableAsync註解開啟非同步任務

前言 @Async為非同步註解,放到需要使用非同步的方法上面,表示呼叫該方法的執行緒與此方法非同步執行,需要配合@EnableAsync註解使用。 沒有非同步執行,沒有@Async註解時 1、建立一個普通的類,並注入到IOC容器中 package com.ex

用於非同步載入的defer和async

定義和用法 <script type="text/javascript" defer="defer"> alert(document.getElementById("p1").first

ES2017 中的非同步函式(async function)

非同步函式中有兩個新的關鍵字async和await async 就是非同步的意思 await 就是等的意思. 暫停函式的執行, 等待非同步任務完成. 宣告非同步函式 /*使用關鍵字 asy

Async之一:流程控制

為了適應非同步程式設計,減少回撥的巢狀,我嘗試了很多庫。最終覺得還是async最靠譜。 Async的內容分為三部分: 流程控制:簡化十種常見流程的處理 集合處理:如何使用非同步操作處理集合中的資料 工具類:幾個常用的工具類 本文介紹其中最簡單最常用的流程控制部分。