前端異步解決方案-4.1(generator)

前言

終於開始寫generator了,離這個系列的終結又進了一步。其實generator我還處在會用可是不理解原理的狀態,可是知識不總結,不記錄的話容易忘記,因此我仍是把如今的一點心得記錄下來。等到之後有了更深的理解再回來補充。
想要看更深度解析generator的朋友能夠移步漫話JavaScript與異步·第三話——Generator:化異步爲同步這裏面談及了generator的底層實現及generator的用法。是我看過的文章中自認爲解釋的最好的一篇,並且篇幅也不長,建議你們去看一看。html

實現

根據一向的做風,咱們先嚐試本身實現generator
嘗試ing............
好了嘗試完了,實現不了,老老實實的學習generator的用法吧。es6

用法

在個人理解中,generator最大的特色就是可讓函數在特定的地方停下,等待被喚醒後在函數內部環境中繼續執行。咱們結合代碼來看一看:
註釋:【1】Iterator Object對象:參考 Iterator 文章比較長,可是若是隻是想要了解什麼是Iterator Object的話看完第一小節就足夠了promise

//輸出分割線的函數,感興趣的能夠自行百度如何設置console.log的樣式
function cut_off(color) {
  console.log("%c------------------------------------------","color:"+color+";font-size:20px");
}

//* 爲generator函數的標識,若是咱們想要建立一個generator函數就必須在function後面加上*
function* generator() {
  let num1, num2;
  num1 = 123;
  console.log("num1", num1, "num2", num2);
  //yield就是該函數內部暫停的地方,暫停的同時會把yield後面的值返回出去
  yield num1;
  num2 = 456;
  console.log("num1", num1, "num2", num2);
  yield num2;
  console.log("num1", num1, "num2", num2);
  return "end"
}

console.log("generator defined");
//函數返回一個Iterator Object對象;
// 可是與普通函數不一樣的是,這個時候函數並不執行函數內部的代碼
let g = generator();
console.log("g defined");
cut_off("red");

console.log("g.next() run 1");
//開始執行函數內部的代碼,而且遇在到yield的時候返回 yield後面的值
console.log(g.next());
cut_off("red");

console.log("g.next() run 2");
//從上次執行完的地方執行,而且遇在到yield的時候返回 yield後面的值
console.log(g.next());
cut_off("red");

console.log("g.next() run 3");
//從上次執行完的地方執行,此次是最後一次有值的返回,done的狀態會變爲true
console.log(g.next());
cut_off("red");

console.log("g.next() run 4");
//已經執行完成以後再次被調用,永遠返回{value:undefined, done: true}
console.log(g.next());
cut_off("red");

console.log("g.next() run 5");
//已經執行完成以後再次被調用,永遠返回{value:undefined, done: true}
console.log(g.next());

貼上一張代碼和運行結果的對比圖輔助你們理解
請點擊查看原圖
但願你們看到這裏已經理解了generator的基本用法,可是這個東西確實有點難,因此我放出聯繫方式圖片描述
我有空的時候能夠一塊兒探討一下dom

接下來我要講generator最重要的第二個特性,咱們能夠經過.next(value)yeild賦值(這是不許確的說法,可是咱們能夠這麼理解,方便咱們使用generator),仍是貼代碼:異步

function* generator() {
  //第一次調用.next()是啓動了這個函數一直運行到下一個yield的位置
  let num1, num2;
  num1 = 123;
  console.log("num1", num1, "num2", num2);
  num2 = yield num1;/*1.因爲運算的順序,js會先計算右邊的值,也就是在num2被賦值以前,函數就中止運行了
                      2.第二次調用.next(value)的時候,value被next傳入了yield的位置
                      3.程序繼續運行,value被賦值給了num2
                     */
  console.log("num1", num1, "num2", num2);
  return num2
}

let g = generator();

console.log("g.next() run 1");
console.log(g.next());
cut_off("red");

console.log("g.next(789) run 2");
//從上次執行完的地方執行,而且將789傳入函數內部
console.log(g.next(789));

貼出函數和運行結果的對比圖,輔助你們理解.next(value)爲yeild賦值(該說法並不許確)
請查看原圖
generator的基礎學習到這裏就能嘗試實際使用了,接下來咱們嘗試讓generator在異步中大展身手吧,話很少說上代碼;函數

//模擬異步請求
let request = function (sucF, errF) {
  if ((sucF && typeof sucF !== "function") || (errF && typeof errF !== "function")) {
    throw new Error("傳入參數必須爲函數")
  }
  setTimeout(function () {
    let data = parseInt(Math.random() * 100);
    if (data < 90 && sucF) {
      sucF(data)
    } else if (errF) {
      errF("本次異步失敗了")
    }
  }, 100)
};

function* generator() {
  let num1, num2;
  num1 = yield request((data) => {
    g.next(data)                 //這個地方可能有些難以理解,爲何在g被建立以前,就被使用了。
  }, err => {                   //這個問題能夠看註釋【1】來輔助理解
    console.error(err);
    g.return(err)               //跳過全部的yield,return直接返回err
  });
  console.log("num1", num1, "num2", num2);
  num2 = yield  request((data) => {
    g.next(data)
  }, err => {
    console.error(err);
    g.return(err)             //跳過全部的yield,return直接返回err
  });
  console.log("num1", num1, "num2", num2);
  return num2
}

let g = generator();
//啓動
g.next();

註釋【1】:爲了解釋g爲何能夠在被建立以前就‘被調用’學習

//其實在運行generator的時候generator內部的代碼塊並無開始運行,而是返回了一個Iterator對象,因此你們能夠這樣簡單理解
//function* generator 至關於 
function generator(){
    let obj = {};
    obj.next=function(){
        console.log(g);
    };
    return obj
}
//這樣在調用generator()的時候,g不會被使用
let g = generator();
//這個時候g被調用了,可是在上一行代碼中,g已經被定義了,因此沒有問題,不會報錯
g.next()

這篇文章暫時寫到這裏,由於個人頁面開始卡了,因此我把generator和promise結合的用法放到下一篇文章中去;(ps:今天就會寫出來的)spa

相關文章
相關標籤/搜索