亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

ES6 系列之 Generator 的自動執行的方法示例

2024-05-06 16:46:30
字體:
來源:轉載
供稿:網友

單個異步任務

var fetch = require('node-fetch');function* gen(){  var url = 'https://api.github.com/users/github';  var result = yield fetch(url);  console.log(result.bio);}

為了獲得最終的執行結果,你需要這樣做:

var g = gen();var result = g.next();result.value.then(function(data){  return data.json();}).then(function(data){  g.next(data);});

首先執行 Generator 函數,獲取遍歷器對象。

然后使用 next 方法,執行異步任務的第一階段,即 fetch(url)。

注意,由于 fetch(url) 會返回一個 Promise 對象,所以 result 的值為:

{ value: Promise { <pending> }, done: false }

最后我們為這個 Promise 對象添加一個 then 方法,先將其返回的數據格式化(data.json()),再調用 g.next,將獲得的數據傳進去,由此可以執行異步任務的第二階段,代碼執行完畢。

多個異步任務

上節我們只調用了一個接口,那如果我們調用了多個接口,使用了多個 yield,我們豈不是要在 then 函數中不斷的嵌套下去……

所以我們來看看執行多個異步任務的情況:

var fetch = require('node-fetch');function* gen() {  var r1 = yield fetch('https://api.github.com/users/github');  var r2 = yield fetch('https://api.github.com/users/github/followers');  var r3 = yield fetch('https://api.github.com/users/github/repos');  console.log([r1.bio, r2[0].login, r3[0].full_name].join('/n'));}

為了獲得最終的執行結果,你可能要寫成:

var g = gen();var result1 = g.next();result1.value.then(function(data){  return data.json();}).then(function(data){  return g.next(data).value;}).then(function(data){  return data.json();}).then(function(data){  return g.next(data).value}).then(function(data){  return data.json();}).then(function(data){  g.next(data)});

但我知道你肯定不想寫成這樣……

其實,利用遞歸,我們可以這樣寫:

function run(gen) {  var g = gen();  function next(data) {    var result = g.next(data);    if (result.done) return;    result.value.then(function(data) {      return data.json();    }).then(function(data) {      next(data);    });  }  next();}run(gen);

其中的關鍵就是 yield 的時候返回一個 Promise 對象,給這個 Promise 對象添加 then 方法,當異步操作成功時執行 then 中的 onFullfilled 函數,onFullfilled 函數中又去執行 g.next,從而讓 Generator 繼續執行,然后再返回一個 Promise,再在成功時執行 g.next,然后再返回……

啟動器函數

在 run 這個啟動器函數中,我們在 then 函數中將數據格式化 data.json(),但在更廣泛的情況下,比如 yield 直接跟一個 Promise,而非一個 fetch 函數返回的 Promise,因為沒有 json 方法,代碼就會報錯。所以為了更具備通用性,連同這個例子和啟動器,我們修改為:

var fetch = require('node-fetch');function* gen() {  var r1 = yield fetch('https://api.github.com/users/github');  var json1 = yield r1.json();  var r2 = yield fetch('https://api.github.com/users/github/followers');  var json2 = yield r2.json();  var r3 = yield fetch('https://api.github.com/users/github/repos');  var json3 = yield r3.json();  console.log([json1.bio, json2[0].login, json3[0].full_name].join('/n'));}function run(gen) {  var g = gen();  function next(data) {    var result = g.next(data);    if (result.done) return;    result.value.then(function(data) {      next(data);    });  }  next();}run(gen);

只要 yield 后跟著一個 Promise 對象,我們就可以利用這個 run 函數將 Generator 函數自動執行。

回調函數

yield 后一定要跟著一個 Promise 對象才能保證 Generator 的自動執行嗎?如果只是一個回調函數呢?我們來看個例子:

首先我們來模擬一個普通的異步請求:

function fetchData(url, cb) {  setTimeout(function(){    cb({status: 200, data: url})  }, 1000)}

我們將這種函數改造成:

function fetchData(url) {  return function(cb){    setTimeout(function(){      cb({status: 200, data: url})    }, 1000)  }}

對于這樣的 Generator 函數:

function* gen() {  var r1 = yield fetchData('https://api.github.com/users/github');  var r2 = yield fetchData('https://api.github.com/users/github/followers');  console.log([r1.data, r2.data].join('/n'));}

如果要獲得最終的結果:

var g = gen();var r1 = g.next();r1.value(function(data) {  var r2 = g.next(data);  r2.value(function(data) {    g.next(data);  });});

如果寫成這樣的話,我們會面臨跟第一節同樣的問題,那就是當使用多個 yield 時,代碼會循環嵌套起來……

同樣利用遞歸,所以我們可以將其改造為:

function run(gen) {  var g = gen();  function next(data) {    var result = g.next(data);    if (result.done) return;    result.value(next);  }  next();}run(gen);

run

由此可以看到 Generator 函數的自動執行需要一種機制,即當異步操作有了結果,能夠自動交回執行權。

而兩種方法可以做到這一點。

(1)回調函數。將異步操作進行包裝,暴露出回調函數,在回調函數里面交回執行權。

(2)Promise 對象。將異步操作包裝成 Promise 對象,用 then 方法交回執行權。

在兩種方法中,我們各寫了一個 run 啟動器函數,那我們能不能將這兩種方式結合在一些,寫一個通用的 run 函數呢?我們嘗試一下:

// 第一版function run(gen) {  var gen = gen();  function next(data) {    var result = gen.next(data);    if (result.done) return;    if (isPromise(result.value)) {      result.value.then(function(data) {        next(data);      });    } else {      result.value(next)    }  }  next()}function isPromise(obj) {  return 'function' == typeof obj.then;}module.exports = run;

其實實現的很簡單,判斷 result.value 是否是 Promise,是就添加 then 函數,不是就直接執行。

return Promise

我們已經寫了一個不錯的啟動器函數,支持 yield 后跟回調函數或者 Promise 對象。

現在有一個問題需要思考,就是我們如何獲得 Generator 函數的返回值呢?又如果 Generator 函數中出現了錯誤,就比如 fetch 了一個不存在的接口,這個錯誤該如何捕獲呢?

這很容易讓人想到 Promise,如果這個啟動器函數返回一個 Promise,我們就可以給這個 Promise 對象添加 then 函數,當所有的異步操作執行成功后,我們執行 onFullfilled 函數,如果有任何失敗,就執行 onRejected 函數。

我們寫一版:

// 第二版function run(gen) {  var gen = gen();  return new Promise(function(resolve, reject) {    function next(data) {      try {        var result = gen.next(data);      } catch (e) {        return reject(e);      }      if (result.done) {        return resolve(result.value)      };      var value = toPromise(result.value);      value.then(function(data) {        next(data);      }, function(e) {        reject(e)      });    }    next()  })}function isPromise(obj) {  return 'function' == typeof obj.then;}function toPromise(obj) {  if (isPromise(obj)) return obj;  if ('function' == typeof obj) return thunkToPromise(obj);  return obj;}function thunkToPromise(fn) {  return new Promise(function(resolve, reject) {    fn(function(err, res) {      if (err) return reject(err);      resolve(res);    });  });}module.exports = run;

與第一版有很大的不同:

首先,我們返回了一個 Promise,當 result.done 為 true 的時候,我們將該值 resolve(result.value),如果執行的過程中出現錯誤,被 catch 住,我們會將原因 reject(e)。

其次,我們會使用 thunkToPromise 將回調函數包裝成一個 Promise,然后統一的添加 then 函數。在這里值得注意的是,在 thunkToPromise 函數中,我們遵循了 error first 的原則,這意味著當我們處理回調函數的情況時:

// 模擬數據請求function fetchData(url) {  return function(cb) {    setTimeout(function() {      cb(null, { status: 200, data: url })    }, 1000)  }}

在成功時,第一個參數應該返回 null,表示沒有錯誤原因。

優化

我們在第二版的基礎上將代碼寫的更加簡潔優雅一點,最終的代碼如下:

// 第三版function run(gen) {  return new Promise(function(resolve, reject) {    if (typeof gen == 'function') gen = gen();    // 如果 gen 不是一個迭代器    if (!gen || typeof gen.next !== 'function') return resolve(gen)    onFulfilled();    function onFulfilled(res) {      var ret;      try {        ret = gen.next(res);      } catch (e) {        return reject(e);      }      next(ret);    }    function onRejected(err) {      var ret;      try {        ret = gen.throw(err);      } catch (e) {        return reject(e);      }      next(ret);    }    function next(ret) {      if (ret.done) return resolve(ret.value);      var value = toPromise(ret.value);      if (value && isPromise(value)) return value.then(onFulfilled, onRejected);      return onRejected(new TypeError('You may only yield a function, promise ' +        'but the following object was passed: "' + String(ret.value) + '"'));    }  })}function isPromise(obj) {  return 'function' == typeof obj.then;}function toPromise(obj) {  if (isPromise(obj)) return obj;  if ('function' == typeof obj) return thunkToPromise(obj);  return obj;}function thunkToPromise(fn) {  return new Promise(function(resolve, reject) {    fn(function(err, res) {      if (err) return reject(err);      resolve(res);    });  });}module.exports = run;

co

如果我們再將這個啟動器函數寫的完善一些,我們就相當于寫了一個 co,實際上,上面的代碼確實是來自于 co……

而 co 是什么? co 是大神 TJ Holowaychuk 于 2013 年 6 月發布的一個小模塊,用于 Generator 函數的自動執行。

如果直接使用 co 模塊,這兩種不同的例子可以簡寫為:

// yield 后是一個 Promisevar fetch = require('node-fetch');var co = require('co');function* gen() {  var r1 = yield fetch('https://api.github.com/users/github');  var json1 = yield r1.json();  var r2 = yield fetch('https://api.github.com/users/github/followers');  var json2 = yield r2.json();  var r3 = yield fetch('https://api.github.com/users/github/repos');  var json3 = yield r3.json();  console.log([json1.bio, json2[0].login, json3[0].full_name].join('/n'));}co(gen);
// yield 后是一個回調函數var co = require('co');function fetchData(url) {  return function(cb) {    setTimeout(function() {      cb(null, { status: 200, data: url })    }, 1000)  }}function* gen() {  var r1 = yield fetchData('https://api.github.com/users/github');  var r2 = yield fetchData('https://api.github.com/users/github/followers');  console.log([r1.data, r2.data].join('/n'));}co(gen);

是不是特別的好用?

ES6 系列

ES6 系列目錄地址:https://github.com/mqyqingfeng/Blog

ES6 系列預計寫二十篇左右,旨在加深 ES6 部分知識點的理解,重點講解塊級作用域、標簽模板、箭頭函數、Symbol、Set、Map 以及 Promise 的模擬實現、模塊加載方案、異步處理等內容。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品亚洲第一区| 亚洲欧美精品suv| 成人国产精品久久久| 中文字幕九色91在线| 九九热在线精品视频| 欧美大肥婆大肥bbbbb| 亚洲精品98久久久久久中文字幕| 这里只有精品在线观看| 成人精品视频99在线观看免费| 在线播放精品一区二区三区| 欧美午夜女人视频在线| 91亚洲va在线va天堂va国| 中文字幕不卡在线视频极品| 日韩亚洲精品电影| www.欧美精品一二三区| 国内伊人久久久久久网站视频| 蜜臀久久99精品久久久无需会员| 国产丝袜一区二区三区| 美女久久久久久久久久久| 2019精品视频| 欧美激情第1页| 琪琪亚洲精品午夜在线| 欧美视频二区36p| 免费av在线一区| 日韩一区二区久久久| 国产一区红桃视频| 国产精品久久久久免费a∨| 亚洲欧美日韩视频一区| 国产精品美女网站| 亚洲人成欧美中文字幕| 亚洲精品白浆高清久久久久久| 国产精品一区二区性色av| 九九热在线精品视频| 福利视频一区二区| 国产精品成人一区二区三区吃奶| 成人观看高清在线观看免费| 亚洲欧美制服丝袜| 美日韩精品免费观看视频| 国产精欧美一区二区三区| 精品香蕉在线观看视频一| 两个人的视频www国产精品| 国产精品劲爆视频| 欧美一区二区三区……| 亚洲18私人小影院| 欧美激情视频网| 97色在线观看| 国产在线视频2019最新视频| 亚洲自拍小视频| 欧美专区福利在线| 日韩成人在线免费观看| 亚洲激情视频网站| 欧美放荡办公室videos4k| 国产精品大片wwwwww| 日本久久久a级免费| 日韩欧美在线视频免费观看| 91精品国产高清| 精品亚洲一区二区| 美女国内精品自产拍在线播放| 国产精品第8页| 久久久久久九九九| 国产婷婷色综合av蜜臀av| 久久精品国产精品亚洲| 中文字幕精品在线| 555www成人网| 亚洲第一中文字幕| 亚洲娇小xxxx欧美娇小| 2020久久国产精品| 亚洲第一区中文字幕| 亚洲自拍偷拍色图| 久久国产天堂福利天堂| 国产日韩精品在线播放| 欧美性精品220| 国产精品无码专区在线观看| 亚洲黄页网在线观看| 国产精品久久久亚洲| 久久久久久久久久久人体| 欧美一级片在线播放| 亚洲欧美999| 亚洲va欧美va国产综合剧情| 亚洲成人激情小说| 成人免费视频xnxx.com| 国产成人精品一区二区| 欧美色欧美亚洲高清在线视频| 久久91亚洲精品中文字幕奶水| 日本精品久久电影| 一区二区国产精品视频| 538国产精品一区二区在线| 精品亚洲永久免费精品| 国产美女直播视频一区| 97视频在线免费观看| 92裸体在线视频网站| 国产97色在线|日韩| 另类视频在线观看| 日韩精品免费在线| 欧美高清理论片| 成人精品视频99在线观看免费| 日韩成人激情影院| 国产精品视频男人的天堂| 欧美精品一区二区免费| 精品视频在线观看日韩| 亚洲精品久久久久久久久久久久| 国产精品视频在线观看| 55夜色66夜色国产精品视频| 国产成人久久精品| 国产精品99免视看9| 色噜噜狠狠色综合网图区| 九九精品在线播放| 午夜欧美不卡精品aaaaa| 国产精品久久国产精品99gif| 国产精品久久一区| 亚洲日本成人女熟在线观看| 日韩欧美第一页| 成人免费福利视频| 色多多国产成人永久免费网站| 日韩在线观看av| 97在线精品国自产拍中文| 亚洲情综合五月天| 亚洲欧美成人精品| 亚洲日本中文字幕| 亚洲自拍偷拍第一页| 97久久国产精品| 国产在线精品播放| 韩国精品久久久999| 欧美人交a欧美精品| 久久综合久久美利坚合众国| 在线观看亚洲视频| 亚洲福利视频二区| 国产精品第二页| 亚洲精品日韩激情在线电影| 欧美成人免费一级人片100| 久久精品国产一区二区电影| 日韩欧美国产激情| 亚洲成色999久久网站| 亚洲女成人图区| 美日韩精品视频免费看| 欧美中文字幕在线视频| 91久久国产精品91久久性色| 日本不卡高字幕在线2019| 91精品国产综合久久香蕉| 国产69久久精品成人看| 久久天堂电影网| 亲爱的老师9免费观看全集电视剧| 国语自产偷拍精品视频偷| 激情懂色av一区av二区av| 热re91久久精品国99热蜜臀| 国产在线观看不卡| 亚洲人成电影网站| 亚洲欧美国产精品| 91亚洲一区精品| 国产精品18久久久久久麻辣| 性日韩欧美在线视频| 亚洲精品一二区| 欧美美女操人视频| 国产精品第100页| 欧美精品情趣视频| 欧美怡春院一区二区三区| 欧美国产在线电影| 国语自产精品视频在免费| 欧美日韩国产在线看| 国产精品视频久久久| 欧美日韩免费一区| 亚洲精品福利视频| 成人免费xxxxx在线观看| 国产午夜精品久久久|