快速掌握ES6 iterator Generator和async 之間的關係及其用法

来源:https://www.cnblogs.com/wangzirui98/archive/2020/03/05/12423454.html
-Advertisement-
Play Games

簡述了iterator,genteator和async的關係,和他們的基本原理以及用例。 ...


1.遍歷器iterator

1.1 for遍歷

首先從遠古講起,剛出js的時候如何遍歷一個數組呢?

 var arr = [1, 2, 3, 4, 7, 8, 9]
    for (let i = 0;i < arr.length;i++) {
      console.log(arr[i]);
    }

1.2 forEach遍歷

看起來笨的一批,所以ES5給你研究了一個foreach方法,但是這個方法不能break,也不能改變數組自身的值,也不能返回任何值。

      var arr = [1, 2, 3, 4, 7, 8, 9]
    var arr2 = arr.forEach((element, index) => {
      console.log('第' + index + '個數值為:' + element);
      return element * 2;
    });
    console.log(arr2) // undefined
        console.log(arr1) // [1, 2, 3, 4, 7, 8, 9]

所以說foreach只給你最基本的操作,其他一概不管,如果你想要改變自身的值或者有break和countinue操作我們可以使用map操作,不展開講了,之前專門寫了篇博客總結了下。

wzr:數組遍歷方法總結

1.3 for-in遍歷

那麼ES6專門為遍曆數組提供了一種方法,就是for-of。說道for-of,不得不提到了for-in

那麼關於他們倆的區別,我也專門寫了一篇博客,不在這展開講了。

wzr:深入理解枚舉屬性與for-in和for-of

值得一提的是for-in是可以遍曆數組的,但是不推薦用for-in去遍曆數組,為什麼呢?因為for -in 返回的可枚舉屬性是字元類型,不是數字類型,如果"0","1"這樣的屬性和1,2數組發生相加,很可能不是直接相加,而是字元串的疊加。例如

const items = [1,2,3,4]

for(item in items){
  let tempitem = item+1 
  console.log(items[tempitem]) // undefined
  console.log(tempitem) // 01 21 32 41 item與數字相加 會得到字元串相加的結果,所以導致上面找不著items相應的項
}

所以為了避免歧義,還是不要用for-in遍曆數組的好。

1.4 for-of

那麼接下來就進入正題了,因為for-in在數組這邊比較難用,所以ES6新添加的for-of來彌補for-in的不足。這是個正兒八經遍曆數組的方法。與 forEach() 不同的是,它支持 break、continue 和 return 語句。而且他本身的語法非常的簡單:

for (variable of iterable) {
    //statements
}
  • variable: 在每次迭代中,將不同屬性的值分配給變數。

  • iterable: 被迭代枚舉其屬性的對象。

而且關鍵的問題是for-of不僅可以遍曆數組,他可以遍歷很多類數組對象。

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函數的 arguments 對象
  • NodeList 對象

而他的原理在於這些類數組對象中都有一個屬性,就是Symbol.iterator,也就是說,只要帶Symbol.iterator他都能遍歷,我們單獨把這個屬性拿出來,自己手動執行next()方法就會看到我們成功的遍歷了這個數組:

const items = [1,2,3,4]

const giao =  items[Symbol.iterator]()

console.log(giao.next()) // {value: 1, done: false}
console.log(giao.next()) // {value: 2, done: false}
console.log(giao.next()) // {value: 3, done: false}
console.log(giao.next()) // {value: 4, done: false}
console.log(giao.next()) // {value: undefined, done: true}

同理,我們可以已通過手動寫一個iterator來更深入的瞭解他的原理:

Array.prototype.myiterator = function(){
  let i = 0 
  let items = this
  return {
    next(){
      const done  = i >= items.length
      const value = done ? undefined : items[i++]
      return {
        value, done
      }
    }
  }
}

const item = [1,2,3,4]

// 控制台
> const giao = item.myiterator() //當我們獲得到遍歷器時,我們只需要代替for-of執行myiterator即可遍歷這個數組。
> giao.next()
{value: 1, done: false}
> giao.next()
{value: 2, done: false}
> giao.next()
{value: 3, done: false}
> giao.next()
{value: 4, done: false}
> giao.next()
{value: undefined, done: true}

效果跟for of是一樣的。另外值得註意的是,你可以在任意對象里添加這個屬性,讓他們可遍歷。

const items = [ 'blue', 'yellow', 'white', 'black']

for(item of items){
  console.log(item)
}

1.5總結

遍歷器如果存在與一個對象內,他就可以讓這個對象可供for-of遍歷,for-of的遍歷方法就是不停地調用遍歷器的next()方法,直到done屬性變為true

2.生成器 Generator

為什麼拿生成器和遍歷器一起講呢,因為本質上,生成器器函數返回的就是一個遍歷器!

生成器的語法很簡單,就是在function後面加個*,然後用yield來返回對應的值。(其實也可以將yield可以看做return,只不過需要next()來進行外調用,還有一個函數只能由一個return ,而yield可以有多個)

function* items(){
    yield "1"
    yield "2"
    yield "3"
}

const num = items()

// 控制台
> num.next()
{value: "1", done: false}
> num.next()
{value: "2", done: false}
> num.next()
{value: "3", done: false}
> num.next()
{value: undefined, done: true}
> num.next()
{value: undefined, done: true}

那麼我們yield的之前同樣也可以加入運算

function* items(){
    let i =0
    yield i // 0
    i++
    yield i // 1
    i++
    yield i // 2
    i++ //這個就不運行了,因為他在yield之後
}

const num = items()

//不用瀏覽器控制台,直接列印也行
console.log(num.next()) // {value:0,done:false}
console.log(num.next()) // {value:1,done:false}
console.log(num.next()) // {value:2,done:false}
console.log(num.next()) // {value:undefined,done:true}

利用這樣的特性,我們可以用Generator來進行ajax的等待操作。

function ajax(url){
  // 請求成功自動調用next()方法。然後返回數據結果
  axios.get(url).then(res => gen.next(res.data))
}
function* step(){
  const class = yield ajax.get(`http://laotie.com/getclass`)
  const score = yield ajax.get(`http://laotie.com/getscore?name=${class[0].name})
}

// 獲得這個函數的"遍歷器"
const gen = step()
// 啟動"遍歷器",不啟動就不會動
gen.next() // 獲取class
gen.next() // 獲取到score

因為第二個get請求依賴第一個請求的結果,所以我們解決辦法第一個是運用Promise的回調來限制他們的先後順序,但是在我們學習了生成器之後我們發現生成器的特性很適合做這樣的事。也就是只有當第一個請求執行完之後,才能順序執行第二個請求。

另外還有一些小的特性

*可以添加到任意位置,都不會影響生成genterator。下麵的寫法都是可以的。

function * foo(x, y) { ··· }
function *foo(x, y) { ··· }
function* foo(x, y) { ··· }
function*foo(x, y) { ··· }

關於Generator的Thunk或者co模塊,因為ES8的async的加入,極大地簡化了Genrtator的操作,針對原理和實戰操作也就沒有那麼多要求了,接下來主要說一說async函數。

3.async

1.語法

準確的說,async就是Generator的語法糖,首先看他的語法。

async function laotie(){
  const data = await dosomething()
}

可以看到,

  • 原來的*async代替
  • 原來的yieldawait代替

這樣做的直接的好處就是顯得更加語義化,可讀性更強。但是其實async做到的遠不止如此。

  1. 首先第一點,就是async不用不斷執行next()了,async函數內置了執行器,使得我們在調用函數時,只需要直接調用即可。如下:

    // 接上一個代碼塊
     laotie()
  2. 現在async 的await還是保留著等待的功能,但是因為沒有了next(),所以在調用await不會像yield那樣返回值了。在async中,只有return返回,而且返回的是一個promise對象。

    拿上面的代碼直接改寫成async加await的格式,

    async function items(){
     let i =0
     await i 
     i++
     await i 
     i++
     await i 
     i++ 
    }
    
    console.log(items()) // Promise {<pending>}

    直接調用方法我們能看到返回的是一個狀態為resolved的Promise對象,而不是Iterator。

    而這個對象,返回的值就是函數里return出來的值。我們可以用then()函數來接受這個值並列印他。

    async function items(){
     let i =3
     return i
    }
    
    items().then(res=>{
     console.log(res) // 3
    })

    當然這麼舉例子准定不是正經的用法,這些例子主要用於區分Generator和async函數之前的區別。

2.用法

正確的用法現在是在await之後加入一個非同步函數,await相當於將這個非同步函數轉化為同步函數,等這個非同步函數執行完畢返回resolved的時候時候才往下執行進一步的操作。例如:

async function asyncPrint(value, ms) {
  await new Promise(resolve => {
    setTimeout(resolve, ms);
  });
  console.log(value);
}

asyncPrint('hello world', 1000); // 一秒後列印hellow world

如果這個async 的函數中間有多個await,那麼就讓多個await以排隊的方式執行。

用法2:

先讓我們把之前generator的例子拿過來

function ajax(url){
  // 請求成功自動調用next()方法。然後返回數據結果
  axios.get(url).then(res => gen.next(res.data))
}
function* step(){
  const class = yield ajax.get(`http://laotie.com/getclass`)
  const score = yield ajax.get(`http://laotie.com/getscore?name=${class[0].name})
}

// 獲得這個函數的遍歷器
const gen = step()
// 啟動遍歷器
gen.next() 

寫著挺累的,但是async可以快速的簡化它。因為await接受的就是一個Prmoise函數,所以我們可以直接在await後面使用axios,然後直接使用對象解構來獲取相應的值。

async function step(){
  const {data:{class}} = await axios.get(`http://laotie.com/getclass`)
  const {data:{core}} = await axios.get(`http://laotie.com/getscore?name=${class[0].name})
    return {class,core}
}

這樣是不是就方便多啦!


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

-Advertisement-
Play Games
更多相關文章
  • 下載MySQL mysql、oracle這2個資料庫都是oracle公司的,不知道是不是晚上要維護,這家公司的伺服器晚上很卡,儘量早上下載。 mysql可以下載msi、zip,但zip配置麻煩,建議使用msi。 第一個是網路安裝包,安裝時根據選擇下載插件;第二個裡面已經包含了所有插件。 安裝MySQ ...
  • Docker安裝 系統版本:CentOS Linux release 7.5.1804 (Core) 如果之前有裝過docker可以先卸載舊版本再安裝,沒有則跳過。 yum remove docker \ docker-client \ docker-client-latest \ docker-c ...
  • 1.增加數據 2.刪除數據 3.更新數據 4.查詢數據 5.模糊查詢 6.交集查詢 7.排序查詢 8.分組查詢 9.連接查詢 ...
  • 1、選取最適用的欄位屬性 MySQL可以很好的支持大數據量的存取,但是一般說來,資料庫中的表越小,在它上面執行的查詢也就會越快。因此,在創建表的時候,為了獲得更好的性能,我們可以將表中欄位的寬度設得儘可能小。 例如,在定義郵政編碼這個欄位時,如果將其設置為CHAR(255),顯然給資料庫增加了不必要 ...
  • 1.使用root賬號登陸到MySQL伺服器: 2.切換到mysql資料庫實例: 3.查看用戶表: 4.創建一個只允許從本機登錄的用戶“yang”,密碼為“yangpass”: 5.刪除一個名為“yang”的本機登錄用戶: 6.創建一個允許從任何主機登錄的用戶“yang”,密碼為“yangpass”: ...
  • 轉載請標明出處:https:////www.cnblogs.com/tangZH/p/12409849.html Glide作為一個強大的圖片載入框架,已經被android官方使用,所以,明白Glide的載入流程以及原理對加深我們對glide的理解是很重要的。 本文基於glide 4.11 Glid ...
  • 響應式網頁設計項目#3 Product Landing Page 電商企業,伯庫電玩的塞爾達促銷頁面 https://codepen.io/yiyunpan// #技術堆棧 1. 使用HTML、JavaScript和CSS完成。這裡是純CSS做的 #內容 1. 使用了H5 語義化的標簽 2. 重點使 ...
  • 通過字體、字型大小、顏色、粗體、斜體、下劃線、刪除線、縮進、行間距、字元間距、對齊等css格式化要素實現網頁格式化排版,完成設計頁面效果;瞭解css佈局前提,需要掌握三種html不同標簽:塊狀元素、內聯元素(行級元素)、內聯塊狀元素,瞭解它們之間如何進行轉換;此外通過盒模型掌握css佈局,盒模型作為CS... ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...