国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 開發(fā) > JS > 正文

ES6 系列之 Generator 的自動(dòng)執(zhí)行的方法示例

2024-05-06 16:46:30
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

單個(gè)異步任務(wù)

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

為了獲得最終的執(zhí)行結(jié)果,你需要這樣做:

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

首先執(zhí)行 Generator 函數(shù),獲取遍歷器對(duì)象。

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

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

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

最后我們?yōu)檫@個(gè) Promise 對(duì)象添加一個(gè) then 方法,先將其返回的數(shù)據(jù)格式化(data.json()),再調(diào)用 g.next,將獲得的數(shù)據(jù)傳進(jìn)去,由此可以執(zhí)行異步任務(wù)的第二階段,代碼執(zhí)行完畢。

多個(gè)異步任務(wù)

上節(jié)我們只調(diào)用了一個(gè)接口,那如果我們調(diào)用了多個(gè)接口,使用了多個(gè) yield,我們豈不是要在 then 函數(shù)中不斷的嵌套下去……

所以我們來(lái)看看執(zhí)行多個(gè)異步任務(wù)的情況:

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'));}

為了獲得最終的執(zhí)行結(jié)果,你可能要寫成:

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)});

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

其實(shí),利用遞歸,我們可以這樣寫:

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);

其中的關(guān)鍵就是 yield 的時(shí)候返回一個(gè) Promise 對(duì)象,給這個(gè) Promise 對(duì)象添加 then 方法,當(dāng)異步操作成功時(shí)執(zhí)行 then 中的 onFullfilled 函數(shù),onFullfilled 函數(shù)中又去執(zhí)行 g.next,從而讓 Generator 繼續(xù)執(zhí)行,然后再返回一個(gè) Promise,再在成功時(shí)執(zhí)行 g.next,然后再返回……

啟動(dòng)器函數(shù)

在 run 這個(gè)啟動(dòng)器函數(shù)中,我們?cè)?then 函數(shù)中將數(shù)據(jù)格式化 data.json(),但在更廣泛的情況下,比如 yield 直接跟一個(gè) Promise,而非一個(gè) fetch 函數(shù)返回的 Promise,因?yàn)闆]有 json 方法,代碼就會(huì)報(bào)錯(cuò)。所以為了更具備通用性,連同這個(gè)例子和啟動(dòng)器,我們修改為:

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 后跟著一個(gè) Promise 對(duì)象,我們就可以利用這個(gè) run 函數(shù)將 Generator 函數(shù)自動(dòng)執(zhí)行。

回調(diào)函數(shù)

yield 后一定要跟著一個(gè) Promise 對(duì)象才能保證 Generator 的自動(dòng)執(zhí)行嗎?如果只是一個(gè)回調(diào)函數(shù)呢?我們來(lái)看個(gè)例子:

首先我們來(lái)模擬一個(gè)普通的異步請(qǐng)求:

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

我們將這種函數(shù)改造成:

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

對(duì)于這樣的 Generator 函數(shù):

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'));}

如果要獲得最終的結(jié)果:

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

如果寫成這樣的話,我們會(huì)面臨跟第一節(jié)同樣的問(wèn)題,那就是當(dāng)使用多個(gè) yield 時(shí),代碼會(huì)循環(huán)嵌套起來(lái)……

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

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 函數(shù)的自動(dòng)執(zhí)行需要一種機(jī)制,即當(dāng)異步操作有了結(jié)果,能夠自動(dòng)交回執(zhí)行權(quán)。

而兩種方法可以做到這一點(diǎn)。

(1)回調(diào)函數(shù)。將異步操作進(jìn)行包裝,暴露出回調(diào)函數(shù),在回調(diào)函數(shù)里面交回執(zhí)行權(quán)。

(2)Promise 對(duì)象。將異步操作包裝成 Promise 對(duì)象,用 then 方法交回執(zhí)行權(quán)。

在兩種方法中,我們各寫了一個(gè) run 啟動(dòng)器函數(shù),那我們能不能將這兩種方式結(jié)合在一些,寫一個(gè)通用的 run 函數(shù)呢?我們嘗試一下:

// 第一版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;

其實(shí)實(shí)現(xiàn)的很簡(jiǎn)單,判斷 result.value 是否是 Promise,是就添加 then 函數(shù),不是就直接執(zhí)行。

return Promise

我們已經(jīng)寫了一個(gè)不錯(cuò)的啟動(dòng)器函數(shù),支持 yield 后跟回調(diào)函數(shù)或者 Promise 對(duì)象。

現(xiàn)在有一個(gè)問(wèn)題需要思考,就是我們?nèi)绾潍@得 Generator 函數(shù)的返回值呢?又如果 Generator 函數(shù)中出現(xiàn)了錯(cuò)誤,就比如 fetch 了一個(gè)不存在的接口,這個(gè)錯(cuò)誤該如何捕獲呢?

這很容易讓人想到 Promise,如果這個(gè)啟動(dòng)器函數(shù)返回一個(gè) Promise,我們就可以給這個(gè) Promise 對(duì)象添加 then 函數(shù),當(dāng)所有的異步操作執(zhí)行成功后,我們執(zhí)行 onFullfilled 函數(shù),如果有任何失敗,就執(zhí)行 onRejected 函數(shù)。

我們寫一版:

// 第二版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;

與第一版有很大的不同:

首先,我們返回了一個(gè) Promise,當(dāng) result.done 為 true 的時(shí)候,我們將該值 resolve(result.value),如果執(zhí)行的過(guò)程中出現(xiàn)錯(cuò)誤,被 catch 住,我們會(huì)將原因 reject(e)。

其次,我們會(huì)使用 thunkToPromise 將回調(diào)函數(shù)包裝成一個(gè) Promise,然后統(tǒng)一的添加 then 函數(shù)。在這里值得注意的是,在 thunkToPromise 函數(shù)中,我們遵循了 error first 的原則,這意味著當(dāng)我們處理回調(diào)函數(shù)的情況時(shí):

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

在成功時(shí),第一個(gè)參數(shù)應(yīng)該返回 null,表示沒有錯(cuò)誤原因。

優(yōu)化

我們?cè)诘诙娴幕A(chǔ)上將代碼寫的更加簡(jiǎn)潔優(yōu)雅一點(diǎn),最終的代碼如下:

// 第三版function run(gen) {  return new Promise(function(resolve, reject) {    if (typeof gen == 'function') gen = gen();    // 如果 gen 不是一個(gè)迭代器    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

如果我們?cè)賹⑦@個(gè)啟動(dòng)器函數(shù)寫的完善一些,我們就相當(dāng)于寫了一個(gè) co,實(shí)際上,上面的代碼確實(shí)是來(lái)自于 co……

而 co 是什么? co 是大神 TJ Holowaychuk 于 2013 年 6 月發(fā)布的一個(gè)小模塊,用于 Generator 函數(shù)的自動(dòng)執(zhí)行。

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

// yield 后是一個(gè) 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 后是一個(gè)回調(diào)函數(shù)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 系列預(yù)計(jì)寫二十篇左右,旨在加深 ES6 部分知識(shí)點(diǎn)的理解,重點(diǎn)講解塊級(jí)作用域、標(biāo)簽?zāi)0濉⒓^函數(shù)、Symbol、Set、Map 以及 Promise 的模擬實(shí)現(xiàn)、模塊加載方案、異步處理等內(nèi)容。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持VeVb武林網(wǎng)。


注:相關(guān)教程知識(shí)閱讀請(qǐng)移步到JavaScript/Ajax教程頻道。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 巩留县| 新安县| 北海市| 行唐县| 静宁县| 陵水| 庆云县| 水城县| 改则县| 通海县| 曲麻莱县| 连山| 崇文区| 潜山县| 绥棱县| 璧山县| 垣曲县| 平邑县| 渭源县| 广安市| 漳平市| 临潭县| 秀山| 革吉县| 永川市| 玉林市| 吉水县| 遂川县| 大厂| 万州区| 莱西市| 开原市| 永昌县| 潼南县| 石河子市| 临城县| 泽州县| 漳浦县| 闽清县| 桂东县| 改则县|