ES6生成器函数generator

generator是ES6新增的一个特殊函数,通过 function* 声明,函数体内通过 yield 来指明函数的暂停点,该函数返回一个迭代器,并且函数执行到 yield语句前面暂停,之后通过调用返回的迭代器next()方法来执行yield语句。
如下代码演示:

function* generator() {
  yield 1;
  yield 2;
  yield 3;
}
var gen = generator();

如上代码:generator函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用generator函数后,该函数并不执行,返回的也不是函数运行的结果,而是一个指向内部状态的指针对象,我们可以通过调用next方法,使指针移向下一个状态。

console.log(gen.next());  // {value:1, done: false}
console.log(gen.next());  // {value:2, done: false}
console.log(gen.next());  // {value:3, done: false}
console.log(gen.next());  // {value: undefined, done: true}

如上代码,返回的迭代器每次调用next会返回一个对象 {value: "xxx", done: true/false}, value是返回值,done表示的是否达到迭代器的结尾,true是代表已经到了结尾,false代表没有。

我们可以通过 返回的对象的done是否为true来判断生成器是否执行结束,通过返回对象的value可以得到yield的返回值。如果返回对象的done值为true的话,那么该值value就为undefined。
迭代器next()方法还可以传入一个参数,这个参数会作为上一个yield语句的返回值,如果不传参数,yield语句中生成器函数内的返回值是 undefined。

如下代码演示:

function* test(x) {
  let y = yield x;
  console.log(1111)
  yield y;
}
var t = test(3);
console.log(t.next()); // {value:3, done: false}
console.log(t.next()); // {value: undefined, done: false}
console.log(t.next()); // {value: undefined, done: true}

下面我们向第二个next方法传入值为5, 因此y的值被赋值为5,如下代码:

function* test(x) {
  let y = yield x;
  yield y;
}
var t = test(3);
console.log(t.next()); // {value:3, done: false}
console.log(t.next(5)); // {value: 5, done: false}

我们也可以通过 for...of循环可以自动遍历 generator函数生成对象,此时不需要调用next方法。如下代码:

function* foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 4+1;
  yield 6;
}
for (let v of foo()) {
  console.log(v); // 1, 2, 3, 4, 5, 6
}

1-2 异常处理 

下面是一个简单的列子来捕获generator里的异常

function* error() {
  try {
    throw new Error(‘error‘);
    yield 11;
  } catch(err) {
    console.log(err);
  }
}
var it = error();
it.next();

1-3 Generators的异步的应用

Generators 最主要的特点是单线程执行,同步风格代码编写,同时又允许将 代码异步的特性隐藏在程序的实现细节中。通过generators函数,我们将程序具体的实现细节从异步代码中抽离出来,可以很好的实现了功能和关注点的分离。
下面是使用ajax的demo,异步ajax请求,当请求完成后的数据,需要将返回的数据值传递给下一个请求去,也就是说下一个请求要依赖于上一个请求返回的数据依次传递下去代码如下:

function requestURL(url, cb) {
  // ajax 请求, 请求完成后 调用cb函数
  $.ajax({
    url: url,
    type: ‘get‘,
    dataType: ‘json‘,
    success: function(d) {
      cb(d);
    }
  })
}
var url = ‘http://httpbin.org/get‘;
requestURL(url, function(res) {
  console.log(res);
  var url2 = ‘http://httpbin.org/get?origin=‘+res.origin;
  requestURL(url2, function(res2) {
    console.log(res2);
  });
});

如上代码,我们看到异步ajax请求依次嵌套回调。
下面我们可以使用 generator去重新实现一下,如下代码:

function requestURL(url, cb) {
  // ajax 请求, 请求完成后 调用cb函数
  $.ajax({
    url: url,
    type: ‘get‘,
    dataType: ‘json‘,
    success: function(d) {
      cb(d);
    }
  })
}
var url = ‘http://httpbin.org/get‘;
function request(url) {
  requestURL(url, function(res) {
    it.next(res);
  })
}
function* main() {
  var res1 = yield request(url);
  console.log(res1);
  var res2 = yield request(url + ‘?origin=‘+res1.origin);
  console.log(res2);
}

var it = main();
it.next(); 

如上代码是使用Generator实现的异步代码请求;实现思路如下:
首先 requestURL 函数,发一个ajax请求,请求成功后调用回调函数 cb;request函数是封装requestURL函数,请求成功后回调,调用it.next()方法,
将指针指向下一个yield,main函数是请求启动代码,当 var it = main()时候,会调用main函数,但是只是会调用,但是并不会执行代码,因此需要 it.next()方法执行第一个 yield request(url) 方法,因此会调用request方法,再继续调用requestURL方法,最后会输出res1, 然后 requestURL方法的回调,又调用 
it.next()方法,因此会执行main里面yield request的第二句代码,代码执行和第一次一样,最后输出res2.

上面的代码有一个地方需要理解的是,ajax请求成功以后 会继续调用 it.next()方法,那么成功后的数据如何传递到 res1呢?我们看到没有return语句返回的?
这是因为当 在ajax的callback调用的时候,它首先会传递ajax返回的结果。返回值发送到我们的generator时候,var res1 = yield request(url);给暂停了,然后在调用it.next()方法,会执行下一个请求。
1-4 Generator+Promise 实现异步请求

上面的generator代码可以做一些简单的异步请求,但是会受到一些限制,比如如下:
    1. 没有明确的方法来处理请求error;ajax请求有时候会超时或失败的情况下,这个时候我们需要使用generator中的it.throw(),同时还需要使用try catch来处理错误的逻辑。
   2. 一般情况下我们请求不会涉及到并行请求,但是如果有的需求是并行的话,比如说同时发2个或者多个ajax请求的话,由于 generator yidle机制都是逐步暂停的,无法同时运行另一个或多个任务,
因此,generator不太容易操作多个任务。

我们现在使用promise修改下 request的方法,让yield返回一个promise,所有代码如下:

function requestURL(url, cb) {
  // ajax 请求, 请求完成后 调用cb函数
  $.ajax({
    url: url,
    type: ‘get‘,
    dataType: ‘json‘,
    success: function(d) {
      cb(d);
    }
  })
}
var url = ‘http://httpbin.org/get‘;
function request(url) {
  return new Promise((resolve, reject) => {
    requestURL(url, resolve);
  });
}
function runGenerator(cb) {
  var it = cb(),
    ret;
  // 异步遍历generator
  (function iterator(val){
    console.log(111)
    console.log(val);  // undefined
    // 返回一个promise
    ret = it.next(val);
    console.log(ret);  // {value: Promise, done: false}
    if (!ret.done) {
      if(‘then‘ in ret.value) {
        // 等待接受promise
        ret.value.then(iterator);
      } else {
        setTimeout(function() {
          iterator(ret.value);
        }, 0)
      }
    }
  })();
}
runGenerator(function *main() {
  var res1 = yield request(url);
  console.log(res1);
  var res2 = yield request(url + ‘?origin=‘+res1.origin);
  console.log(res2);
});

上面代码 :

function request(url) {
  return new Promise((resolve, reject) => {
    requestURL(url, resolve);
  });
}

当ajax请求完成后,会返回这个promise,同时接收 yield request(url); 然后通过next()方法恢复generator运行,并把他们传递下去,第一次运行iterator方法后,val值为undefined,然后调用 ret = it.next(val); 返回一个promise ,继续打印下 console.log(ret); 返回如下: {value: Promise, done: false}
虽然上面代码运行正常,但是我们还未处理代码异常的情况下,因此下面处理下代码异常的情况下:

function requestURL(url, cb) {
  // ajax 请求, 请求完成后 调用cb函数
  $.ajax({
    url: url,
    type: ‘get‘,
    dataType: ‘json‘,
    success: function(d) {
      cb(null, d);
    },
    error: function(err) {
      cb(err, text);
    }
  })
}
var url = ‘http://httpbin.org/get‘;
function request(url) {
  return new Promise((resolve, reject) => {
    requestURL(url, function(err, text){
      if (err) {
        reject(err);
      } else {
        resolve(text);
      }
    });
  });
}
function runGenerator(cb) {
  var it = cb(),
    ret;
  // 异步遍历generator
  (function iterator(val){
    console.log(111)
    console.log(val);  // undefined
    // 返回一个promise
    ret = it.next(val);
    console.log(ret);  // {value: Promise, done: false}
    if (!ret.done) {
      if(‘then‘ in ret.value) {
        // 等待接受promise
        ret.value.then(iterator);
      } else {
        setTimeout(function() {
          iterator(ret.value);
        }, 0)
      }
    }
  })();
}

runGenerator(function *main() {
  try {
    var res1 = yield request(url);
  } catch(err) {
    console.log(err);
    return;
  }
  console.log(res1);
  try {
    var res2 = yield request(url + ‘?origin=‘+res1.origin);
  } catch(err) {
    console.log(err);
    return;
  }
  console.log(res2);
})

第一步处理异常的情况下 代码如上已经完成了,现在来解决第二步 使用Promise + generator 解决多个请求同时运行,需要使用Promise.all()方法来解决。
如下代码:

function requestURL(url, cb) {
  // ajax 请求, 请求完成后 调用cb函数
  $.ajax({
    url: url,
    type: ‘get‘,
    dataType: ‘json‘,
    success: function(d) {
      cb(null, d);
    },
    error: function(err) {
      cb(err, text);
    }
  })
}
var url = ‘http://httpbin.org/get‘;
function request(url) {
  return new Promise((resolve, reject) => {
    requestURL(url, function(err, text){
      if (err) {
        reject(err);
      } else {
        resolve(text);
      }
    });
  })
  // 获取返回值后
  .then(function(d) {
    console.log(‘dddddd‘);
    console.log(d);
    return d
  });
}
function runGenerator(cb) {
  var it = cb(),
    ret;
  // 异步遍历generator
  (function iterator(val){
    // 返回一个promise
    ret = it.next(val);
    if (!ret.done) {
      if(‘then‘ in ret.value) {
        // 等待接受promise
        ret.value.then(iterator);
      } else {
        setTimeout(function() {
          iterator(ret.value);
        }, 0)
      }
    }
  })();
}

runGenerator(function *main() {
  var res1 = yield Promise.all([
    request(url),
    request(url)
  ]);
  console.log(2222)
  console.log(res1);
  var result = yield request(url + ‘?origin=‘+res1[0].origin);
  console.log(result);
})
时间: 2024-10-12 22:08:40

ES6生成器函数generator的相关文章

学习ES6生成器(Generator)

背景 在JS的使用场景中,异步操作的处理是一个不可回避的问题,如果不做任何抽象.组织,只是“跟着感觉走”,那么面对“按顺序发起3个ajax请求”的需求,很容易就能写出如下代码(假设已引入jQuery): // 第1个ajax请求 $.ajax({ url:'http://echo.113.im', dateType:'json', type:'get', data:{ data:JSON.stringify({status:1,data:'hello world'}), type:'json',

[ES6系列-07]Generator Function: 生成器函数

[原创]码路工人 Coder-Power 大家好,这里是码路工人有力量,我是码路工人,你们是力量. github-pages 博客园cnblogs Generator function 生成器函数是ES6中新增的语法糖,本质上讲,就是以封装成一个遍历器的形式,让编码的你获得程序的执行控制权,通俗地说就是,流程控制上,踹一脚,走一步,不要太暴力~ 0.前言 要说到生成器函数,就不得不提到javascript的异步编程方式演进史. (不能跑题不能跑题不能跑题) 1.普通的回调函数方式(callbac

ES6生成器基础

ES6引进的最令人兴奋的特性就是一种新的函数生成方式,称为生成器(generator).名称有点奇怪,但是第一眼看上去行为更加奇怪.文章主要介绍生成器如何工作,然后让你明白为什么他们对于未来的JS会有很大的影响. 完成运行 首先看看生成器和普通函数有什么不同.无论你是否已经意识到,关于你的函数,总是可以很基本的假设一些东西:一但函数开始运行,它总是在其他JS代码可以运行前运行完毕.例子: setTimeout(function(){ console.log("Hello World")

ES6新特性三: Generator(生成器)函数详解

本文实例讲述了ES6新特性三: Generator(生成器)函数.分享给大家供大家参考,具体如下: 1. 简介 ① 理解:可以把它理解成一个函数的内部状态的遍历器,每调用一次,函数的内部状态发生一次改变. ② 写法: function* f() {} ③ 作用:就是可以完全控制函数的内部状态的变化,依次遍历这些状态. ④ 运行过程:当调用Generator函数的时候,该函数并不执行,而是返回一个遍历器(可以理解成暂停执行).通过调用next()开始执行,遇到yield停止执行,返回一个value

取代Promise的Generator生成器函数

接触过Ajax请求的会遇到过异步调用的问题,为了保证调用顺序的正确性,一般我们会在回调函数中调用,也有用到一些新的解决方案如Promise相关的技术. 在异步编程中,还有一种常用的解决方案,它就是Generator生成器函数.顾名思义,它是一个生成器,它也是一个状态机,内部拥有值及相关的状态,生成器返回一个迭代器Iterator对象,我们可以通过这个迭代器,手动地遍历相关的值.状态,保证正确的执行顺序. 一.简单使用 1. 声明 Generator的声明方式类似一般的函数声明,只是多了个*号,并

引入Promise对象和Generator生成器函数

当然,如果您只有一个 subscription 的话,就不需要执行这个命令. 下面我们使用Entity Framework Core试用Code First模式,结合领域驱动设计,快速构建领域模型,假设建模后的模型包含 “用户“”和“物品“两个实体,当然这种开发模式可以充分利用面向对象的设计理念,比如领域模型通过继承获取一些系统属性:BaseEntity 这个 ASP.NET Core 站点,既用了缓存,也有数据库访问操作,是一个典型的 Web 站点,如果它能持续稳定运行,将为我们全站迁移至 A

ES6学习笔记<三> 生成器函数与yield

为什么要把这个内容拿出来单独做一篇学习笔记? 生成器函数比较重要,相对不是很容易理解,单独做一篇笔记详细聊一聊生成器函数. 标题为什么是生成器函数与yield? 生成器函数类似其他服务器端语音中的接口(Interface),yield则是生成器函数中表示 返回 或 继续 执行的关键字. 弄清楚这两个概念后,先看一个例子: function* fun(val) { yield 1*val; yield 2*val; yield 3*val; yield 4*val; return 5*val; }

es6 中的generator函数控制流程

Generator函数跟普通函数的写法有非常大的区别: 一是,function关键字与函数名之间有一个星号: 二是,函数体内部使用yield语句,定义不同的内部状态(yield在英语里的意思就是"产出"). 最简单的Generator函数如下: function* g() { yield 'a'; yield 'b'; yield 'c'; return 'ending'; } g(); // 返回一个对象 g函数呢,有四个阶段,分别是'a','b','c','ending'. Gen

Day 21 三元表达式、生成器函数、列表解析

知识点程序: 1 #! /usr/bin/env python 2 # -*- coding: utf-8 -*- 3 # __author__ = "DaChao" 4 # Date: 2017/6/16 5 6 l = [i for i in range(100) if i%2 > 0] 7 print(l) 8 9 ########################### 10 #生成器 generator!!! 模拟tail grep功能 11 ##############