博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ES6中的异步编程:Generators函数(一)
阅读量:6293 次
发布时间:2019-06-22

本文共 6165 字,大约阅读时间需要 20 分钟。

对ES6的generators的介绍分为3个部分

  • 第一部分base介绍及使用

  • 第二部分基于generators和Promise实现最强大的异步处理逻辑

概述

Generator函数是协程在ES6的实现,用来做异步流程的封装,最大特点就是可以交出函数的执行权(即暂停执行)。十分的奇葩,光看语法,简直认不出这也是JavaScript了。由于可以使用yield语句来暂停异步操作,这让generators异步编程的代码,很像同步数据流方法一样。因为从语法角度来看,generators函数是一个状态机,封装了多个内部状态,通过iterator来分步调用。

基本语法

2个关键字搞定generators语法

  • function与函数名直接的星号:*

  • 函数体内yield语句

function* testGenerator() {    yield 'first yield';    yield 'second yield';    return 'last';}var gen = testGenerator();console.log(gen.next().value);// first yield // { value: 'first yield', done: false }console.log(gen.next().value);// second yield// { value: 'second yield', done: false }console.log(gen.next().value);// last // { value: 'last', done: true }console.log(gen.next().value);// undefined

for...of遍历

for...of循环可以自动遍历generators函数的iterator对象,且不再需要调用next方法。for...of需要检查iterator对象的done属性,如果为true,则结束循环,因此return语句不能被遍历到

for (let i of testGenerator) {    console.log(i);}// first yield// second yield

next方法的参数

yield句本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield语句的返回值。

function *gen(){  let arr = [];  while(true){    arr.push(yield arr);  }}var name = gen();console.log(name.next('first').value);//[]console.log(name.next('second').value);//["second"]console.log(name.next('thrid').value);//["second","thrid"]

需要注意的是,第一次执行next设置参数没有效果。

generators实践

实现Fibonacci数列

递归实现:

function* fib (n, current = 0, next = 1) {  if (n === 0) {    return 0;  }  yield current;  yield* fib(n - 1, next, current + next);}for (let n of fibonacci()) {  if (n > 1000) break;  console.log(n);}

注:如果存储计算结果再过运算,这样的实现比递归方法效率高3倍

function* fibonacci() {  let [prev, curr] = [0, 1];  for (;;) {    [prev, curr] = [curr, prev + curr];    yield curr;  }}for (let n of fibonacci()) {  if (n > 1000) break;  console.log(n);}

利用for...of循环,遍历任意对象(object)的方法

原生的JavaScript对象没有遍历接口,无法使用for...of循环,通过Generator函数为它加上这个接口,就可以用了。

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

ES6中iterator遍历接口汇总

  • for...of循环

  • 扩展运算符(...)

  • 解构赋值

  • Array.from方法内部调用的

它们都可以将Generator函数返回的Iterator对象,作为参数来使用。

function* numbers () {  yield 1  yield 2  return 3}// 扩展运算符[...numbers()] // [1, 2]// Array.from 方法Array.from(numbers()) // [1, 2]// 解构赋值let [x, y] = numbers();x // 1y // 2// for...of 循环for (let n of numbers()) {  console.log(n)}// 1// 2

generators与同步

generators一个特点就是代码看上去非常像同步编程的效果

function* test() {    yield( "1st" );    yield( "2nd" );    yield( "3rd" );    yield( "4th" );}var iterator = test();console.log( "== Start of Line ==" );console.log( iterator.next().value );console.log( iterator.next().value );for ( var line of iterator ) {    console.log( line );}console.log( "== End of Line ==" );

看下输出,浓浓的同步执行风格。

== Start of Line ==1st2nd3rd4th== End of Line ==

callback、Promises、Generators比较

举例说一个场景,查询一篇新闻文章的作者信息,流程是:请求最新文章列表->请求某文章相关id->作者id信息

callback实现

getArticleList(function(articles){    getArticle(articles[0].id, function(article){        getAuthor(article.authorId, function(author){            alert(author.email);        })    })})function getAuthor(id, callback){    $.ajax(url,{        author: id    }).done(function(result){        callback(result);    })}function getArticle(id, callback){    $.ajax(url,{        id: id    }).done(function(result){        callback(result);    })}function getArticleList(callback){    $.ajax(url)    .done(function(result){        callback(result);    });}

用Promise来做

getArticleList().then(articles => getArticle(articles[0].id)).then(article => getAuthor(article.authorId)).then(author => {    alert(author.email);});function getAuthor(id){    return new Promise(function(resolve, reject){        $.ajax({            url: id+'author.json',            success: function(data) {              resolve(data);          }        })    });}function getArticle(id){    return new Promise(function(resolve, reject){        $.ajax({            url: id+'.json',            success: function(data) {              resolve(data);          }        })    });}function getArticleList(){    return new Promise(function(resolve, reject){       $.ajax({           url: 'all.json',           success: function(data) {             resolve(data);         }       })     });}

Gererator来实现

function* run(){  var articles = yield getArticleList();  var article = yield getArticle(articles[0].id);  var author = yield getAuthor(article.authorId);  alert(author.email);  }var gen = run();gen.next().value.then(function(r1){  gen.next(r1).value.then(function(r2){      gen.next(r2).value.then(function(r3){        gen.next(r3);        console.log("done");      })  })});

runGenerator的实现

每次都要手动去调用next方法,还是会让代码变得冗长,我们可以设计一个专门用来运行generators的方法,并可以抽象出来,以后就可以做一个统一的error管理,或者获取本地数据逻辑的变化。

Thunk函数方法

编译器的‘传名调用’实现,将所有的参数放到一个临时函数中,再将这个临时函数作为参数传入到函数体中。该临时函数就叫做Thunk函数。

任何函数,只要参数有回调函数,就能写成Thunk函数的方法。下面就是简单的Thunk函数转换器。

//es5var Thunk = function(fn) {    return function() {        var args = Array.pototype.silce.call(argumnets);        return function (callback) {            args.push(callback);            return fn.apply(this. args);        }    }}//es6var Thunk = function(fn) {    return function(...args) {        return function(callback) {            return fn.call(this, ...args, callback);        }    }}

一个使用Thunk方法来实现readFile的例子

//正常版本的readFile(多参数)fs.readFile(filename, callback);//Thunk版本的readFile(单参数)var readFileThunk = Thunk(filename);readFileThunk(callback);var Thunk = function(fileName) {    return function(callback) {        return fs.readFile(fileName, callback);    }}

可以看到,如果我们通过构建一个基于Thunk方法实现的runGenerators函数,可以很好的控制我们的generators运行流程。

function *generator() {    var articles = yield getArticleList();    var article = yield getArticle(articles[0].id);    var author = yield getAuthor(article.authorId);    console.log(author.email);}function runGenerator() {    var gen = generator();        function go(result) {        if(result.done) return;                result.value.then(function(rsp) {            go(gen.next(rsp));        })    }        go(gen.next());}runGenerator();

参考

转载地址:http://zhcta.baihongyu.com/

你可能感兴趣的文章
Spring Boot Unregistering JMX-exposed beans on shutdown
查看>>
poi 导入导出的api说明(大全)
查看>>
Mono for Android 优势与劣势
查看>>
将图片转成base64字符串并在JSP页面显示的Java代码
查看>>
js 面试题
查看>>
sqoop数据迁移(基于Hadoop和关系数据库服务器之间传送数据)
查看>>
腾讯云下安装 nodejs + 实现 Nginx 反向代理
查看>>
Javascript 中的 Array 操作
查看>>
java中包容易出现的错误及权限问题
查看>>
AngularJS之初级Route【一】(六)
查看>>
服务器硬件问题整理的一点总结
查看>>
SAP S/4HANA Cloud: Revolutionizing the Next Generation of Cloud ERP
查看>>
Mellanox公司计划利用系统芯片提升存储产品速度
查看>>
白帽子守护网络安全,高薪酬成大学生就业首选!
查看>>
ARM想将芯片装进人类大脑 降低能耗是一大挑战
查看>>
Oracle数据库的备份方法
查看>>
Selenium 自动登录考勤系统
查看>>
关于如何以编程的方式执行TestNG
查看>>
智能照明造福千家万户 家居智能不再是梦
查看>>
物联网如何跳出“看起来很美”?
查看>>