記錄--通過手寫,分析async await核心原理

来源:https://www.cnblogs.com/smileZAZ/archive/2022/09/22/16720415.html
-Advertisement-
Play Games

這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 async await 語法是 ES7出現的,是基於ES6的 promise和generator實現的 generator函數 在之前我專門講個generator的使用與原理實現,大家沒瞭解過的可以先看那個手寫generator核心 ...


這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

前言

async await 語法是 ES7出現的,是基於ES6的 promise和generator實現的

generator函數

在之前我專門講個generator的使用與原理實現,大家沒瞭解過的可以先看那個手寫generator核心原理,再也不怕面試官問我generator原理

這裡就不再贅述generator,專門的文章講專門的內容。

await在等待什麼

我們先看看下麵這代碼,這是async await的最簡單使用,await後面返回的是一個Promise對象:

async function getResult() {
    await new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
            console.log(1);
        }, 1000);
    })
}

getResult()

但不知你有沒有想過一個問題,為什麼會等到返回的promise的對象的狀態為非pending的時候才會繼續往下執行,也就是resolve執行之後,才會繼續執行,就像下麵的代碼一樣

async function getResult() {
    await new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
            console.log(1);
        }, 1000);
    })
    console.log(2);
}

getResult()

可以看到運行結果是先列印了1,再列印2了,也就是說明在返回的promise對象沒執行resolve()前,就一直在await,等它執行。然後再執行下麵的程式,那這個是怎麼實現的呢?

原理實現

我們看一下下麵的代碼,輸出順序是什麼?

async function getResult() {
    await new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
            console.log(1);
        }, 1000);
    })
    

    await new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2);
            console.log(2);
        }, 500);
    })

    await new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
            console.log(3);
        }, 100);
    })

}

getResult()

沒錯是 1,2,3.

那用generator函數專門來實現這個效果呢

我一開始這樣來實現:

function* getResult(params) {
    
    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
            console.log(1);
        }, 1000);
    })

    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2);
            console.log(2);
        }, 500);
    })

    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
            console.log(3);
        }, 100);
    })
}
const gen = getResult()

gen.next();
gen.next();
gen.next();

但是發現列印順序是 3,2,1.明顯不對。

這裡的問題主要是三個 new Promise幾乎是同一時刻執行了。才會出現這種問題,所以需要等第一個promise執行完resolve之再執行下一個,所以要這麼實現

function* getResult(params) {

    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
            console.log(1);
        }, 1000);
    })

    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2);
            console.log(2);
        }, 500);
    })

    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
            console.log(3);
        }, 100);
    })
}
const gen = getResult()

gen.next().value.then(() => {
    gen.next().value.then(() => {
        gen.next();
    });
});

可以看到這樣就列印正常了。

特別 需要解釋下。gen.next().value 就是返回的promise對象,不理解的可以看看文首介紹的那篇generator的 文章。手寫generator核心原理,再也不怕面試官問我generator原理

優化

但是呢,總不能有多少個await,就要自己寫多少個嵌套吧,所以還是需要封裝一個函數,顯然,遞歸實現最簡單

function* getResult(params) {

    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
            console.log(1);
        }, 1000);
    })

    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2);
            console.log(2);
        }, 500);
    })

    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
            console.log(3);
        }, 100);
    })
}
const gen = getResult()

function co(g) {
    g.next().value.then(()=>{
        co(g)
    })
}

co(gen)

再來看看列印結果

可以發現成功執行了,但是為什麼報錯了?

這是因為generator方法會返回四次,最後一次的value是undefined。

而實際上返回第三次就表示已經返回done,代表結束了,所以,我們需要判斷是否是已經done了,不再讓它繼續遞歸

所以可以改成這樣

function* getResult(params) {

    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
            console.log(1);
        }, 1000);
    })

    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2);
            console.log(2);
        }, 500);
    })

    yield new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
            console.log(3);
        }, 100);
    })
}
const gen = getResult()

function co(g) {
    const nextObj = g.next();
    if (nextObj.done) {
        return;
    }
    nextObj.value.then(()=>{
        co(g)
    })
}

co(gen)

可以看到這樣就實現了。

完美,這個co其實也是大名鼎鼎的co函數的簡單寫法

本篇文章關於async 和 await的原理揭秘就到此為止了,再講下去就不禮貌了。

本文轉載於:

https://juejin.cn/post/7136424542238408718

如果對您有所幫助,歡迎您點個關註,我會定時更新技術文檔,大家一起討論學習,一起進步。

 


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

-Advertisement-
Play Games
更多相關文章
  • 近日, 在一個小型項目中, 遇到了一個觸及我知識盲區的bug. 項目用的是MySQL 5.7.25, 其中有一張表 config_data, 包含四個欄位, id, name, value, expireAt. 其中id為主鍵, name建有唯一索引, 表的用途大概就是存放一些有時效性的配置. 以上 ...
  • Hadoop的第一個產品是HDFS,可以說分散式文件存儲是分散式計算的基礎,也可見分散式文件存儲的重要性。如果我們將大數據計算比作烹飪,那麼數據就是食材,而Hadoop分散式文件系統HDFS就是燒菜的那口大鍋。這些年來,各種計算框架、各種演算法、各種應用場景不斷推陳出新,讓人眼花繚亂,但是大數據存儲的 ...
  • 前記 (可跳過這段來自本up的羅里吧嗦。。。) 在做上一個項目的時候,需要用到本地資料庫,以前做公司項目用的是輕量級資料庫Realm,做自己小項目用的是greenDAO,大學學的是SQL server,但是在flutter中,相關插件用的資料庫是SQLite(sqflite插件),但本人還沒接觸過S ...
  • 7月28日,在袋鼠雲2022產品發佈會上,袋鼠雲技術負責人思樞正式宣佈旗下產品「大數據基礎平臺EasyMR」發佈。 EasyMR是袋鼠雲自研的大數據基礎平臺,提供Hadoop、Hive、Spark、Trino、HBase、Kafka等組件,完全相容Apache開源生態;支持企業級安全管控,一鍵開啟L ...
  • 日常開發我們對一條DML語句較為熟悉,很多開發人員都瞭解sql的執行過程,比較熟悉,但是DDL是如何執行的呢,大部分開發人員可能不太關心,也認為沒必要瞭解,都交給DBA吧。 其實不然,瞭解一些能儘量避開一些ddl的坑,那麼下麵帶大家一起瞭解一下DDL執行的方式,。 ...
  • 2022-09-22 (4)set操作 ①添加操作(sadd): 例: sadd student A B ②查看操作(smembers):例: smembers student ③移除操作(srem):例: srem student A (5)zset(有序集合而且裡面的元素是不能重覆的)操作 ①添 ...
  • 最新版Other問題,請查看我最近的一片文章 我主要開發小型應用,周期較短,提審較頻,使用帳號也多,正常申請的三方購買的都有使用。提審時經常會遇到這樣那樣的問題,單獨說other-other,也曾遇到過不下10次,我將會導致出現這個問題的原因分為幾類: 1、帳號問題: 大概半年前的那段時間蘋果對帳號 ...
  • AU上傳ipa出現下圖紅框提示說明成功上傳,如果App Store後臺沒有出現構建版本, 請登錄 apple賬號對應的郵箱查看反饋,特別留意垃圾郵箱,無論成功還是失敗,apple都會發郵件 一、首先登錄iTunes Connect 後臺、查看ipa構建情況 https://appstoreconne ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...