美文网首页
16Generator函数的方法

16Generator函数的方法

作者: 我_巨可爱 | 来源:发表于2017-10-30 17:02 被阅读0次

简介

基本概念

Generator 是 ES6 提供的一种异步编程解决方案。可以把它理解成一个状态机,并且会返回一个遍历器对象

  1. 有以下两个特征
  • function关键字和函数名之间有一个星号*
  • 函数体内部使用yield表达式
  1. 执行大致过程
  • 生成 Iterator 之后,第一次调用next方法,直到遇见第一个yield表达式为止。此时返回一个对象,value就是yield表达式的值,同时done为false
  • 按照此规律执行,当执行到return语句是,next方法还是返回一个对象,该对象的value就是return返回的值,同时done为ture
  • 如果没有return,执行完所有的yielddone还是为false。需要再来一下next

yield 表达式

  1. generator 函数中可以不使用yield,此时变成了一个单纯的暂缓执行函数
  2. 在普通函数,匿名函数中使用yield表达式,会产生一个错误
  3. 如果yield表达式在另一个表达式中,必须放在圆括号内
// 情景3示例
function* demo() {
  console.log('Hello' + (yield 99)); // OK
  console.log('Hello' + (yield 123)); // OK
}

next 方法參數

next 方法可以带一个参数,该参数会被当作上一个yield表达式的返回值

基础案例分析

function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}
var b = foo(5);
b.next();  // {value:6,done:false}
b.next(12);  // {value:8,done:false}
// 此时 x=5,y=24,z=13
b.next(13);  // {value:42,done:true}
  1. 第一个使用next不需要传值

进阶案例分析

function* dataConsumer() {
  console.log('Started');
  console.log(`1. ${yield}`);
  console.log(`2. ${yield}`);
  return 'result';
}
let genObj = dataConsumer();
genObj.next();
genObj.next('a');
genObj.next('b');
  1. 第一次调用next,执行到第二条语句暂停。因此打印'Started'
  2. 第二次调用next,传入a,作为上次yield的返回值,打印1. a。继续执行到yield暂停
  3. 第三次调用next,传入b,作为上次yield的返回值,答应2. b。继续执行,此时{value:'result',done:true}

for...of循环

斐波那契数列

function* feibonacci() {
  let [prev,curr] = [0,1];
  for (;;) {
    [prev,curr] = [curr,prev+curr]
    yield curr;
  }
}
// num 是值的大小,不是个数
for (let num of feibonacci()) {
  if (num > 100) break; // ---> break 会触发遍历器的return方法,return 方法其实一般就是返回`return {done:true}`
  console.log(num);
}

对象扩展遍历器

在对象的[Symbol.iterator]上挂载遍历器

function* objectEntries() {
  let objKeys = Object.keys(this);
  for (let key of objKeys) {
    yield [key,this[key]]
  }
}
let jane = { first: 'Jane', last: 'Doe'};
// 只能绑定到单个的对象上
jane[Symbol.iterator] = objectEntries;
for (let [key,val] of jane) {
  console.log(key,val)
}

throw方法

遍历器对象都有一个throw方法,在外部抛出错误之后,内部能够捕获

基础知识

外部抛出错误

var g = function* () {
  try {
    yield;
  } catch (e) {
    console.log('内部捕获',e)
  }
};
var i = g();
i.next();
try {
  i.throw('a');
  i.throw('b');
} catch (e) {
  console.log('外部捕获',e);
}
// 内部捕获 a
// 外部捕获 b
  1. 遍历器i执行next方法
  2. 遍历器i在外部抛出错误,被内部的捕获。同时,throw方法传递的参数当作catch的参数
  3. 遍历器再次抛出错误,catch已经执行过了,就不执行了,因此被外部的try catch捕获
  4. 可以内部捕获外部抛出的错误,也可以外部捕获抛出的错误
  5. throw方法,也就是抛出错误的方法,默认执行一次next

内部抛出错误

function* foo() {
  var x = yield 3;
  var y = x.toUpperCase();
  yield y;
}

var it = foo();

it.next(); // { value:3, done:false }

try {
  it.next(42);
} catch (err) {
  console.log(err);
}
  1. 由于next(42)后,x为数字42没有toUpperCase方法。因此,将抛出一个错误
  2. 抛出的错误将在外部被捕获

return方法

  1. 使用return方法
  • 当有参数时,返回给定的值
  • 当没有参数,返回undefined
  1. Generator函数内有try...finally代码块,那么return方法会推迟到finally代码执行完再执行
function* gen() {
  yield 1;
  yield 2;
}
var g = gen();
g.next(); // {value: 1,done: false}
g.return('foo'); // {value: 'foo',done: true}
g.next(); // {value: undefined,done: false}
// finally
function* numbers () {
  try {
    yield 2;
  } finally {
    yield 4;
  }
  yield 6;
}
var g = numbers();
g.next() // { value: 2, done: false }
g.return(7) // { value: 4, done: false } --- 执行finally中语句
g.next() // { value: 7, done: true } --- 执行完finnally中语句之后,再执行return语句

next,throw,return 共同点

它们作用都是让 Generator 函数回复执行

yield*表达式

yield*表达式,代表它后面是一个遍历器对象,在没有return语句的情况下,相当于使用for of

  1. Generator函数中不能直接调用Generator函数,需要使用yield*

基本案例

function* foo() {
  yield 'a';
  yield 'b';
}
function* bar() {
  yield 'x';
  yield* foo();
  yield 'y';
}
// Generator bar 相当于
function* bar() {
  yield 'x';
  for (let v of foo()) {
    yield v;
  } 
  yield 'y';
}
// 调用 bar
for (let v of bar()) {
  console.log(v);
}
// 结果是:x,a,b,y

yield*后边值的种类

任何数据结构只要有 Iterator 接口,就可以被yield*遍历。

  1. yield* 'hello'
  2. yield* [1,2,3]

有return的Generator函数

yield* foo(),如果foo函数有return,那么该表达式是有返回值的

function *foo() {
  yield 2;
  yield 3;
  return "foo";
}

function *bar() {
  yield 1;
  var v = yield *foo();
  console.log( "v: " + v );
  yield 4;
}

var it = bar();

it.next()
// {value: 1, done: false}
it.next()
// {value: 2, done: false}
it.next()
// {value: 3, done: false}
it.next();
// 相当于没有调用 next 方法,只是将返回值返回给表达式
// "v: foo"
// {value: 4, done: false}
it.next()
// {value: undefined, done: true}

取出嵌套数组

function* tree(arr) {
    if (Array.isArray(arr)) {
        for (let i = 0;i < arr.length;i++) {
            yield* tree(arr[i]);
        }
    }else {
        yield arr;
    }
}
const arr = [1,[2,3],[4,5],6,7]
for (let i of tree(arr)) {
    console.log(i);
}
  1. 注意两个地方
  • 如果使用yield tree,也就是没有*,那么调用Generator方法是没有什么效果的
  • 使用let声明的变量进行递归没有关系

作为对象属性的Generator函数

简写成下面的形式

let obj = {
  * myGeneratorMethod() {

  }
}

Generator 函数的this

Generator的返回值

  1. Generator 函数总是返回一个遍历器,这个遍历器是 Generator 的实例
  2. 使用instanceof检测,为true
  3. Generator 原型上的方法,将被遍历器继承
function* p() {

}
p.property.say = function () {
  console.log('hello')
}
let s = p();
s instanceof p;  // true
s.say();  // 'hello'

Generator 中 this 基础知识

  1. 即使“返回值”是“Generator”函数实例,但是“Generator”中this上绑定的属性,“返回值”上并不会有
  2. 不能对 Generator 函数直接使用 new 命令

应用

异步操纵的同步化表达

// loading 应用
function* loadUI() {
  showLoadingScreen();
  yield loadUIDataAsynchronously();
  hideLoadingScreen();
}
var loader = loadUI();
loader.next();
loader.next();
  1. 第一次执行next方法时,展示加载页面,同时发送异步请求
  2. 其实第二次执行next方法,应该是异步请求完毕的时候
// Ajax 应用
function request(url){
  // 是个伪代码,其实就是一个发送 ajax 请求的方法
  makeAjaxCall(url,function (response) {
    it.next(reponse);
  })
}
function* main() {
  var result = yield request('http://some.url');
  var resp = JSON.parse(result);
  console.log(resp.value)
}
var it = main();
it.next();
  1. 第一次执行next方法,发送ajax请求
  2. 在成功响应的回调函数中,第二次执行next方法,该方法将response当作上一次的返回值

控制流管理

这里只介绍最简单的一种方法

let steps = [step1Func, step2Func, step3Func];

function *iterateSteps(steps){
  for (var i=0; i< steps.length; i++){
    var step = steps[i];
    yield step();
  }
}
// 之后使用 for of 即可

相关文章

  • 16Generator函数的方法

    简介 基本概念 Generator 是 ES6 提供的一种异步编程解决方案。可以把它理解成一个状态机,并且会返回一...

  • 【2019-05-21】函数和闭包

    (1)方法定义函数最通用的方法是作为某个对象的成员。这种函数称为方法。 (2)本地函数把函数定义在别的函数内部,类...

  • Kotlin学习之初探——函数

    函数的定义 方法VS函数 *方法是函数的一种特殊类型*有receiver的函数即为方法*类比java的话*比如类中...

  • js 函数与方法

    函数:通过函数名字来调用。 方法:将函数定义到对象中就是方法,相应的属性名则为方法名,可以说方法是比较特殊的函数。...

  • python 13面向对象

    构造函数 析构函数 私有 类方法 静态方法 属性方法

  • JavaScript函数定义和方法介绍

    一.函数定义三种方法 函数的三种定义方法分别是:函数声明、函数表达式和Function()构造函数的方法。下面依次...

  • Javascript构造函数(二)函数内方法和原型方法

    1.概念 函数内方法 把方法写在构造函数内的情况我们简称为函数内方法 原型prototype方法 把方法写在pro...

  • MS-常用方法

    方法汇总 数组方法 字符串方法 时间函数 Mysql数据库 File函数 Directory函数 其他方法 iss...

  • runtime常用方法

    类 类结构 类实例结构 常用函数 方法 结构 类方法的常用函数 方法的常用函数 方法选择器 动态创建类 示例: 动...

  • Python基础(38) - 判断调用的是函数还是方法

    如何区分调用的是函数还是方法 方法 函数 判断是否是函数或者方法 打印type 判断name 使用isinstan...

网友评论

      本文标题:16Generator函数的方法

      本文链接:https://www.haomeiwen.com/subject/ggcmpxtx.html