国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

再讀Generator和Co源碼

ernest.wang / 2431人閱讀

摘要:沿用上面的例子,把包裝成一個對象這個回調就是等價于通過在里執行回調函數,獲取到上一步操作的結果和交回執行權,并把值傳遞回函數內部,實現了遞歸執行進一步封裝,可以得到以下的代碼遞歸執行

以前看過的內容,感覺忘得差不多,最近抽空又看了一次,果然書讀百遍其義自見

Generator的執行

Generator函數可以實現函數內外的數據交換執行權交換

從第一次調用next開始,從函數頭部開始執行,執行到第一個yield語句時,把執行權交出到函數外部,并返回該yield語句右值,同時在此處暫停函數

在下一次調用next時候(可以傳遞參數),把執行權返還給函數內部,同時把參數賦值給上一次暫停的yield語句的左值,并從該行到開始執行到下一個yield前,并一直循環該過程

需要注意的是,yield語句的左值,不能由右值賦值,如 let a = yield 3a 的值并不等于3,a 的只能由函數外部調用next時傳入的參數賦值。

function test() {
    return 3;
}

function* gen(){
    console.log(0);
    
    let yield1 = yield 1;
    console.log("yield1 value: ", yield1);// yield1: 2
    
    let yield2 = yield test();
    console.log("yield2 value: ", yield2);// yield2: 4
    
    return 3;
}

let gen1 = gen();

let next1 = gen1.next();
console.log("next1 value: ", next1);// next: { value: 1, done: false }

let next2 = gen1.next(2);
console.log("next2 value: ", next2);// next: { value: 3, done: false }

let next3 = gen1.next(4);
console.log("next3 value: ", next3);// next: { value: undefined, done: true }
第一次調用

從函數頂部開始往下執行,所以首先輸出 console.log(0)

然后執行 yield1 = yield 1,此時會把表達式右值返回, 即返回 1

所以此時 next1 = {value: 1, done: false}, 接著輸出 next1

gen函數內部在yield1 = yield 1暫停

第二次調用

從函數內部 yield1 = yield 1 開始執行

注意: 與第一次調用不同,此次調用傳入了參數2, 第一次調用已經執行了該yield語句,所以并不會返回右值,而是會進行賦值操作,把傳入的參數 2 賦給 yield1

接著執行 console.log("yield1 value: ", yield1), 此時yield1 = 2

然后執行 yield2 = yield test(), 此時會把表達式右值返回, 即返回 3

所以此時 next2 = {value: 3, done: false}, 接著輸出 next2

gen函數內部在yield2 = yield test()暫停

第三次調用

從函數內部 yield2 = yield test() 開始執行

注意: 傳入了參數4, 進行賦值操作,此時yield2 = 4

接著執行 console.log("yield2 value: ", yield2), 此時的 yield2 值為4

因為函數內部已經沒有yield語句,所以一直執行執行到函數尾部return 5

所以最后 next3 = {value: 5, done: true}, 接著輸出 next2

至此函數執行完畢

我們發現Generator函數的執行就是一個循環調用next的過程,自然的想到使用遞歸來實現自動執行

function* gen() {
  let a = yield 1;
  let b = yield 2;
  let c = yield 3;
}

var g = gen();
var res = g.next();

while(!res.done){
  console.log(res.value);
  res = g.next();
}

最簡單的幾行代碼,就實現了Generator的"自動執行",但有一個致命的缺點,代碼里如果有一步異步操作,并且下一步的操作依賴上一步的結果才能執行,這樣的代碼就會出錯,無法執行,代碼如下

function* gen() {
  let file1 = yield fs.readFile("a", () => {});
  let file2 = yield fs.readFile(file1.name, () => {});
}

var g = gen();
var res = g.next();

// 異步操作,執行file2的yield時
// file1的值為undefined
while(!res.done){
  res = g.next(res.value);
}

這就十分尷尬了...使用Generator的一個初衷就是為了避免多層次的回調,寫出同步代碼,而我們現在又卡在了回調上,所以需要使用Thunk函數

函數Thunk化

開發中多數情況都不會多帶帶使用Thunk函數,但是把Thunk和Generator結合在一起使用時,就會發生奇妙的化學反應,可以用來實現Generator函數的自動執行。

Thunk化用一句話總結就是,將一個具有多個參數且有包含一個回調函數的函數轉換成一個只接受回調函數作為參數的單參數函數,附一段網上的實現

const Thunk = function(fn) {
  return function (...args) {
    return function (callback) {
      return fn.call(this, ...args, callback);
    }
  };
};

具體原理不多贅述,按照個人理解,函數Thunk化,就是把帶有回調函數的函數拆分為兩步執行

// 普通函數
function func(a, b, callback){
  const sum = a + b;
  callback(sum);
}
// 普通調用
func(1, 2, alert);

// 對函數進行Thunk化
const ft = thunkify(func);
// Thunk化函數調用
ft(1, 2)(alert);

包含異步操作的例子,在執行fs.readFile(fileName)這第一步操作值之后,數據已經拿到,但是不對數據進行操作,而是在第二步的(err, data) => {}回調函數中進行數據操作

let fs = require("fs");
// 正常版本的readFile
fs.readFile(fileName, (err, data) => {});

// Thunk版本的readFile
fs.readFile(fileName)((err, data) => {});
Generator的自動執行

目前結合ThunkPromise都可以實現

Generator + Thunk

上面報錯的例子,把readFileThunk化之后,問題就能夠得到解決,

let thunkify = require("thunkify");
let readFileThunk = thunkify(fs.readFile);

function* gen() {
  let file1 = yield readFileThunk("a");
  let file2 = yield readFileThunk(file1.name);
}

var g = gen();
var r1 = g.next();

r1.value(function (err, data) { // 這個回調就是readFileThunk("a")的回調
  var r2 = g.next(data);  // 等價于file1 = data;
  r2.value(function (err, data) {
    if (err) throw err;
    g.next(data);
  });
});

執行next后返回對象中的value,不再是一個簡單的值,而是一個回調函數,即readFileThunk的第二步操作,在這個回調函數里,可以取得異步操作的結果,更重要的是可以在這個回調函數中繼續調用next,把函數的執行權返還給gen函數內部,同時把file1的值通過next的參數傳遞進去,整個遞歸就能一直運行。

Generator + Promise

沿用上面的例子,把readFile包裝成一個Promise對象

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

function* gen() {
  let file1 = yield readFileThunk("a");
  let file2 = yield readFileThunk(file1.name);
}

var g = gen();
var r1 = g.next();

r1.value.then(function (data) { // 這個回調就是resolve(data)
  var r2 = g.next(data);  // 等價于file1 = data;
  r2.value.then(function ( data) {
    if (err) throw err;
    g.next(data);
  });
});

通過在then里執行回調函數,獲取到上一步操作的結果和交回執行權,并把值傳遞回gen函數內部,實現了遞歸執行

進一步封裝,可以得到以下的代碼

let Bluebird = require("bluebird");
let readFileThunk = Bluebird(fs.readFile);

function run(fn) {
  const gen = fn();
  function next(err, data) {
    const result = gen.next(data);
    if (result.done) {
      result.value;
    } else {
      result.value.then((data) => {
        next(data);
      });
    }
  }
  
  // 遞歸執行
  next();
}

run(function* g() {
  let file1 = yield readFileThunk("a");
  let file2 = yield readFileThunk(file1.name);
});

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95369.html

相關文章

  • Koa源碼閱讀筆記(1) -- co

    摘要:正好自己之前也想看的源代碼,所以趁著這個機會,一口氣將其讀完。源碼解讀的源代碼十分簡潔,一共才兩百余行。結語的源代碼讀取來不難,但其處理方式卻令人贊嘆。而且閱讀的源代碼,是閱讀源碼的必經之路。 本筆記共四篇Koa源碼閱讀筆記(1) -- coKoa源碼閱讀筆記(2) -- composeKoa源碼閱讀筆記(3) -- 服務器の啟動與請求處理Koa源碼閱讀筆記(4) -- ctx對象 起...

    taoszu 評論0 收藏0
  • co源碼分析及其實踐

    摘要:返回的結果是一個對象,類似于表示本次后面執行之后返回的結果。對象用于一個異步操作的最終完成或失敗及其結果值的表示簡單點說就是處理異步請求。源碼分析主要脈絡函數調用后,返回一個實例。參考鏈接解釋對象的用法的源碼及其用法 本文始發于我的個人博客,如需轉載請注明出處。為了更好的閱讀體驗,可以直接進去我的個人博客看。 前言 知識儲備 閱讀本文需要對Generator和Promise有一個基本的...

    vincent_xyb 評論0 收藏0
  • thunkify與co源碼解讀

    開頭 首先本文有將近3000字,閱讀可能會占用你20分鐘左右。 文筆可能不佳,希望能幫助到閱讀此文的人有一些收獲 在進行源碼閱讀前首先抱有一個疑問,thunk函數是什么,thunkify庫又是干什么的,co又是干嘛,它有啥用 程序語言有兩種求值策略 傳名調用 傳入參數實際上是傳入函數體 傳值調用 函數體在進入的時候就進行運算計算值 編譯器的傳名調用實現,往往是將參數放到一個臨時函數之中,再將這個...

    Tangpj 評論0 收藏0
  • 從react-start到co源碼(三)

    摘要:第三篇腳手架依賴的核心庫的源碼解析。該篇是這個系列文章的第三篇主要是對的源碼進行分析講解。的源碼十分簡單但實現的功能卻是十分的強大。源碼概括源碼主要包含了兩部分公共方法和私有方法。 react作為當前十分流行的前端框架,相信很多前端er都有蠢蠢欲動的學習它的想法。工欲善其事,必先利其器。這篇文章就簡單的給大家介紹一下如何我快速的搭建一個react前端開發環境。主要針對于react小白,...

    wind5o 評論0 收藏0
  • co-parallel & co-gather源碼解析

    摘要:昨天也是好好的看了一下的源碼,今天打算自己來做一下解析。源碼如下這段代碼真的是很短,但是方法真的很巧妙。因為兩個方法用到了,這里把的源碼也貼出來源碼的描述就是為了執行而創建的。最后再次感謝提供的思路。 原文鏈接,轉載請注明出處 最近看了Ma63d關于爬蟲的這篇文章,正好自己也在做爬蟲,看到他在文中提到了co-parallel和co-gather,就打算改一下自己的代碼(本來代碼就只是為...

    caozhijian 評論0 收藏0

發表評論

0條評論

ernest.wang

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<