async/await,瞭解一下?

来源:https://www.cnblogs.com/lunlunshiwo/archive/2018/04/24/8922500.html
-Advertisement-
Play Games

上一篇博客我們在現實使用和麵試角度講解了Promise(原文可參考《面向面試題和實際使用談promise》),但是Promise 的方式雖然解決了 callback hell,但是這種方式充滿了 Promise的 then() 方法,如果處理流程複雜的話,整段代碼將充滿 then,代碼流程不能很好的 ...


  上一篇博客我們在現實使用和麵試角度講解了Promise(原文可參考《面向面試題和實際使用談promise》),但是Promise 的方式雖然解決了 callback hell,但是這種方式充滿了 Promise的 then() 方法,如果處理流程複雜的話,整段代碼將充滿 then,代碼流程不能很好的表示執行流程。

為什麼是async/await

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

function* foo(x) {
    yield x + 1;
    yield x + 2;
    return x + 3;
}

  我們可以根據不斷地調用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的定時函數:

fn = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(1)
    }, 2000)
  })
}
const Fn = () =>{
  fn().then((res) => {
    console.log(res)
  })
}
Fn()
console.log(2)

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

    使用async/await的定時函數:

fn = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(1)
    }, 2000)
  })
}
const Fn = async () => {
  await fn().then((res) => {
    console.log(res)
  })
}
Fn()
console.log(2)

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

  那麼,why?

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

實際運用

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

//偽代碼
let axiosArr = [];
for (let i = 0, len = arr.length; i < len; i++) {
  let params = qs.stringify({
    'param': arr[i].index,
  })
  axiosArr.push(axios.post(url, params, {
    headers
  }))
}
/*
*上面的迴圈是迴圈抓取2345條數據,平均每個數據要訪問16個介面
*用axios.all同時詢問,當返回結束後將返回值處理
*然後將返回值存儲到mongodb資料庫中
*/
await axios.all(axiosArr).then(
  axios.spread(function () {
    for (let i = 0, len = arguments.length; i < len; i++) {
      let str = `${unescape(arguments[i].data.replace(/\\u/g, '%u'))}`;
      str = basics.subStr(basics.deletN(basics.deletS(basics.cutStr(str))));
      concentArr[i].concent = str
    }
    mg.mongodbMain({
      name: obj.name,
      alias: obj.alias,
      type: type,
      url: obj.url,
      drugsConcent: concentArr
    })
  }))

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

async/await的身後事

  我們說過了async 函數返回值是 Promise 對象。

const delay = timeout => new Promise(resolve=> setTimeout(resolve, timeout));
async function f(){
    await delay(1000);
    await delay(2000);
    await delay(3000);
    return 'done';
}

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

  那麼其內部一旦拋出異常,則會導致返回的 Promise 對象狀態變為 reject 狀態。拋出的錯誤而會被 catch 方法回調函數接收到。

async function e(){
    throw new Error('error');
}
e().then(v => console.log(v))
.catch( e => console.log(e));//拋出的錯誤會被catch捕捉到

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

let fn1 = ()=>{
    return new Promise((resolve,reject) => {
        setTimeout(()=>{
            reject('故意拋出錯誤');
        },500);
    });
}

let fn2 = ()=>{
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve(1);
        },500);
    });
}

let getList = async ()=>{
    let a = await fn1();
    let b = await fn2();
    return {first: a,second:b};
}
getList().then(result=> {
    console.log(result);
}).catch(err=> {
    console.log(err);// 由於fn1的報錯,async的狀態直接變成了rejected
});

 

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

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 問題一: 今天拿到一份有近百萬條數據的Excel要導到資料庫裡面,我先在本地(2014)用自帶Excel,然後生成腳本文件去伺服器(2008)上執行;文件SQL打開不了 解決方法: 用自帶的sqlcmd工具 第一步:Win+R 鍵入:cmd 命令,開啟命令行工具; 第二步:鍵入:cd C:\Prog ...
  • Oracle資料庫伺服器DG從庫重啟後,無法完成數據同步,具體報錯信息如下: 一、報錯信息 alter log報錯 二、錯誤原因分析 根據報錯信息可以看到通過tns連接logdb服務名存在問題,可通過排查監聽是否啟動,是否有防火牆限制等。 三、處理步驟 通過排查監聽和防火牆限制,發現沒有問題,主庫通 ...
  • 什麼是資料庫: 資料庫(database)是按照數據結構來組織、存儲和管理數據的倉庫,每個資料庫都有一個或多個不同的API用於創建、訪問、管理、搜索和複製所保存的數據。 什麼是mysql: mysql是關係型資料庫管理系統,可以更好的支持WEB應用。 關係型資料庫與非關係型資料庫: 關係型資料庫是通 ...
  • list類型是一個鏈表結構,主要功能有push,pop等。而且list是一個雙向鏈表,可以通過相關操作進行集合的頭部或者尾部添加,刪除元素。 lpush key string 在key對應的list的頭部添加字元串元素,返回1表示成功,0表示key存在且不是list類型 rpush key stri ...
  • [20180423]flashback tablespace與snapshot standby.txt--//預設建立表空間是打開flashback on,如果某個表空間flashback off,在dg啟動snapshot standby時註意,可能"回不來",--//通過測試說明問題.1.環境: ...
  • 首先是下載圖解 1、首先卸載centos7中自帶的mariadb rpm -qa|grep mariadb //查詢出來已安裝的mariadb rpm -e --nodeps 文件名 //卸載mariadb,文件名為上述命令查詢出來的文件 2、查看是否已經安裝了mysql rpm -qa | gre ...
  • 一,添加圖標 你的應用在iPhone主屏幕上的標準圖標(Icon.png)是57像素*57像素的正方形,PNG格式,不能有透明效果或者圖層,72DPI.除些之外,你還可以提供一個同樣格式的114像素*114像素的高解析度圖標([email protected]).當用戶的設備是Retina屏幕的時候,這個圖標就 ...
  • Android 自定義支持快速搜索篩選的選擇控制項 項目中遇到選擇控制項選項過多,需要快速查找匹配的情況。 做了簡單的Demo,效果圖如下: 源碼地址: "https://github.com/whieenz/SearchSelect" 這個控制項是由Dialog+SearchView+ListView實 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...