你是怎麼理解ES6中 Generator的?使用場景?

来源:https://www.cnblogs.com/smileZAZ/p/18073639
-Advertisement-
Play Games

這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 一、介紹 Generator 函數是 ES6 提供的一種非同步編程解決方案,語法行為與傳統函數完全不同 回顧下上文提到的解決非同步的手段: 回調函數 promise 那麼,上文我們提到promsie已經是一種比較流行的解決非同步方案,那麼為什麼 ...


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

一、介紹

Generator 函數是 ES6 提供的一種非同步編程解決方案,語法行為與傳統函數完全不同

回顧下上文提到的解決非同步的手段:

  • 回調函數
  • promise

那麼,上文我們提到promsie已經是一種比較流行的解決非同步方案,那麼為什麼還出現Generator?甚至async/await呢?

該問題我們留在後面再進行分析,下麵先認識下Generator

Generator函數

執行 Generator 函數會返回一個遍歷器對象,可以依次遍歷 Generator 函數內部的每一個狀態

形式上,Generator函數是一個普通函數,但是有兩個特征:

  • function關鍵字與函數名之間有一個星號
  • 函數體內部使用yield表達式,定義不同的內部狀態
function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

二、使用

Generator 函數會返回一個遍歷器對象,即具有Symbol.iterator屬性,並且返回給自己

function* gen(){
  // some code
}

var g = gen();

g[Symbol.iterator]() === g
// true

通過yield關鍵字可以暫停generator函數返回的遍歷器對象的狀態

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
var hw = helloWorldGenerator();

上述存在三個狀態:helloworldreturn

通過next方法才會遍歷到下一個內部狀態,其運行邏輯如下:

  • 遇到yield表達式,就暫停執行後面的操作,並將緊跟在yield後面的那個表達式的值,作為返回的對象的value屬性值。
  • 下一次調用next方法時,再繼續往下執行,直到遇到下一個yield表達式
  • 如果沒有再遇到新的yield表達式,就一直運行到函數結束,直到return語句為止,並將return語句後面的表達式的值,作為返回的對象的value屬性值。
  • 如果該函數沒有return語句,則返回的對象的value屬性值為undefined
hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }

done用來判斷是否存在下個狀態,value對應狀態值

yield表達式本身沒有返回值,或者說總是返回undefined

通過調用next方法可以帶一個參數,該參數就會被當作上一個yield表達式的返回值

function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}

var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }

正因為Generator函數返回Iterator對象,因此我們還可以通過for...of進行遍歷

function* foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  return 6;
}

for (let v of foo()) {
  console.log(v);
}
// 1 2 3 4 5

原生對象沒有遍歷介面,通過Generator函數為它加上這個介面,就能使用for...of進行遍歷了

function* objectEntries(obj) {
  let propKeys = Reflect.ownKeys(obj);

  for (let propKey of propKeys) {
    yield [propKey, obj[propKey]];
  }
}

let jane = { first: 'Jane', last: 'Doe' };

for (let [key, value] of objectEntries(jane)) {
  console.log(`${key}: ${value}`);
}
// first: Jane
// last: Doe

三、非同步解決方案

回顧之前展開非同步解決的方案:

  • 回調函數
  • Promise 對象
  • generator 函數
  • async/await

這裡通過文件讀取案例,將幾種解決非同步的方案進行一個比較:

回調函數

所謂回調函數,就是把任務的第二段單獨寫在一個函數裡面,等到重新執行這個任務的時候,再調用這個函數

fs.readFile('/etc/fstab', function (err, data) {
  if (err) throw err;
  console.log(data);
  fs.readFile('/etc/shells', function (err, data) {
    if (err) throw err;
    console.log(data);
  });
});

readFile函數的第三個參數,就是回調函數,等到操作系統返回了/etc/passwd這個文件以後,回調函數才會執行

Promise

Promise就是為瞭解決回調地獄而產生的,將回調函數的嵌套,改成鏈式調用

const fs = require('fs');

const readFile = function (fileName) {
  return new Promise(function (resolve, reject) {
    fs.readFile(fileName, function(error, data) {
      if (error) return reject(error);
      resolve(data);
    });
  });
};


readFile('/etc/fstab').then(data =>{
    console.log(data)
    return readFile('/etc/shells')
}).then(data => {
    console.log(data)
})

這種鏈式操作形式,使非同步任務的兩段執行更清楚了,但是也存在了很明顯的問題,代碼變得冗雜了,語義化並不強

generator

yield表達式可以暫停函數執行,next方法用於恢復函數執行,這使得Generator函數非常適合將非同步任務同步化

const gen = function* () {
  const f1 = yield readFile('/etc/fstab');
  const f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

async/await

將上面Generator函數改成async/await形式,更為簡潔,語義化更強了

const asyncReadFile = async function () {
  const f1 = await readFile('/etc/fstab');
  const f2 = await readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

區別:

通過上述代碼進行分析,將promiseGeneratorasync/await進行比較:

  • promiseasync/await是專門用於處理非同步操作的

  • Generator並不是為非同步而設計出來的,它還有其他功能(對象迭代、控制輸出、部署Interator介面...)

  • promise編寫代碼相比Generatorasync更為複雜化,且可讀性也稍差

  • Generatorasync需要與promise對象搭配處理非同步情況

  • async實質是Generator的語法糖,相當於會自動執行Generator函數

  • async使用上更為簡潔,將非同步代碼以同步的形式進行編寫,是處理非同步編程的最終方案

四、使用場景

Generator是非同步解決的一種方案,最大特點則是將非同步操作同步化表達出來

function* loadUI() {
  showLoadingScreen();
  yield loadUIDataAsynchronously();
  hideLoadingScreen();
}
var loader = loadUI();
// 載入UI
loader.next()

// 卸載UI
loader.next()

包括redux-saga中間件也充分利用了Generator特性

import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'
import Api from '...'

function* fetchUser(action) {
   try {
      const user = yield call(Api.fetchUser, action.payload.userId);
      yield put({type: "USER_FETCH_SUCCEEDED", user: user});
   } catch (e) {
      yield put({type: "USER_FETCH_FAILED", message: e.message});
   }
}

function* mySaga() {
  yield takeEvery("USER_FETCH_REQUESTED", fetchUser);
}

function* mySaga() {
  yield takeLatest("USER_FETCH_REQUESTED", fetchUser);
}

export default mySaga;

還能利用Generator函數,在對象上實現Iterator介面

function* iterEntries(obj) {
  let keys = Object.keys(obj);
  for (let i=0; i < keys.length; i++) {
    let key = keys[i];
    yield [key, obj[key]];
  }
}

let myObj = { foo: 3, bar: 7 };

for (let [key, value] of iterEntries(myObj)) {
  console.log(key, value);
}

// foo 3
// bar 7

參考文獻

  • https://es6.ruanyifeng.com/#docs/generator-async

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

-Advertisement-
Play Games
更多相關文章
  • 支持事務安全表(ACID),支持行鎖定和外鍵; MySQL事務的ACID特性是確保數據準確性和可靠性的基本原則,包括**原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持久性(Durability)**。具體如下: 1. **原子性(Atomicity) ...
  • 在MySQL 8.0中,可以通過創建自定義哈希函數來處理VARCHAR類型的欄位,以便用作分區鍵。下麵是一個簡單的示例,演示如何在MySQL8.0中創建自定義哈希函數來處理VARCHAR類型的欄位 分區後的表效果 方法一,自定義哈希函數,失敗而告終 創建自定義哈希函數: DELIMITER // C ...
  • 對於資料庫表中的大類,小類我們基本一直在使用id ,parentid的方式,今天發現了一種更清晰,更完美的解決方式。 SQL Server 2008版本之後的新類型HierarchyID 不知道大家有沒有瞭解, 該類型作為取代id, parentid的一種解決方案,讓人非常驚喜。 官方給的案例淺顯易 ...
  • 正處於企業指標建設過程中的你,是否經常遇到這樣的問題: • 各個部門獨立建設信息系統,由此產生的指標定義和計算方式各異,導致管理層無法快速準確地掌握整體業務運行狀況 • 缺乏對指標的統一管理和規範,產生重覆的指標計算工作,導致數據計算資源被過度消耗,增加運維成本和數據處理壓力 • 不知道指標體系建設 ...
  • GreatSQL里也能用上RocksDB引擎 1. 前言 RocksDB 是基於Facebook 開源的一種支持事務的、高度可壓縮、高性能的MyRocks存儲引擎,特別適用於高度壓縮和大容量的數據。以下是一些關鍵特點: 高性能: LSM 樹結構使得RocksDB在寫入密集型負載下表現卓越。它能夠處理 ...
  • 原文: Android 桌面小組件使用-Stars-One的雜貨小窩 藉助公司上的幾個項目,算是學習了Android桌面小組件的用法,記下踩坑記錄 基本步驟 1.創建小組件佈局 這裡需要註意的事,小組件佈局里不能使用自定義View,只能使用原生的組件,比如說LinearLayout,TextView ...
  • XML 是一種用於存儲和傳輸數據的與軟體和硬體無關的工具。 什麼是XML? XML代表eXtensible Markup Language(可擴展標記語言)。XML是一種與HTML非常相似的標記語言。XML被設計用於存儲和傳輸數據。XML被設計成具有自我描述性。XML不執行任何操作,也許有點難理解, ...
  • HTML特性: 1.空白摺疊現象 1.文字間折為一個空格 <p>hello world!</p> 2.標簽內壁空白忽略 <p> hello world! </p> 2.轉義字元 <p>小於號&lt;</p> <p>大於號&gt;</p> <p>空格&nbsp;</p> <p>版權號&copy;</p ...
一周排行
    -Advertisement-
    Play Games
  • C#TMS系統代碼-基礎頁面BaseCity學習 本人純新手,剛進公司跟領導報道,我說我是java全棧,他問我會不會C#,我說大學學過,他說這個TMS系統就給你來管了。外包已經把代碼給我了,這幾天先把增刪改查的代碼背一下,說不定後面就要趕鴨子上架了 Service頁面 //using => impo ...
  • 委托與事件 委托 委托的定義 委托是C#中的一種類型,用於存儲對方法的引用。它允許將方法作為參數傳遞給其他方法,實現回調、事件處理和動態調用等功能。通俗來講,就是委托包含方法的記憶體地址,方法匹配與委托相同的簽名,因此通過使用正確的參數類型來調用方法。 委托的特性 引用方法:委托允許存儲對方法的引用, ...
  • 前言 這幾天閑來沒事看看ABP vNext的文檔和源碼,關於關於依賴註入(屬性註入)這塊兒產生了興趣。 我們都知道。Volo.ABP 依賴註入容器使用了第三方組件Autofac實現的。有三種註入方式,構造函數註入和方法註入和屬性註入。 ABP的屬性註入原則參考如下: 這時候我就開始疑惑了,因為我知道 ...
  • C#TMS系統代碼-業務頁面ShippingNotice學習 學一個業務頁面,ok,領導開完會就被裁掉了,很突然啊,他收拾東西的時候我還以為他要旅游提前請假了,還在尋思為什麼回家連自己買的幾箱飲料都要叫跑腿帶走,怕被偷嗎?還好我在他開會之前拿了兩瓶芬達 感覺感覺前面的BaseCity差不太多,這邊的 ...
  • 概述:在C#中,通過`Expression`類、`AndAlso`和`OrElse`方法可組合兩個`Expression<Func<T, bool>>`,實現多條件動態查詢。通過創建表達式樹,可輕鬆構建複雜的查詢條件。 在C#中,可以使用AndAlso和OrElse方法組合兩個Expression< ...
  • 閑來無聊在我的Biwen.QuickApi中實現一下極簡的事件匯流排,其實代碼還是蠻簡單的,對於初學者可能有些幫助 就貼出來,有什麼不足的地方也歡迎板磚交流~ 首先定義一個事件約定的空介面 public interface IEvent{} 然後定義事件訂閱者介面 public interface I ...
  • 1. 案例 成某三甲醫預約系統, 該項目在2024年初進行上線測試,在正常運行了兩天後,業務系統報錯:The connection pool has been exhausted, either raise MaxPoolSize (currently 800) or Timeout (curren ...
  • 背景 我們有些工具在 Web 版中已經有了很好的實踐,而在 WPF 中重新開發也是一種費時費力的操作,那麼直接集成則是最省事省力的方法了。 思路解釋 為什麼要使用 WPF?莫問為什麼,老 C# 開發的堅持,另外因為 Windows 上已經裝了 Webview2/edge 整體打包比 electron ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...