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
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...