generator 生成器

发布于 2022-12-22  33 次阅读


如何返回一个迭代对象

function * foo() {
	yield 'Hello world';
}

const iter = foo();
console.log(iter);

yield(产出),每次暂停函数执行,有记忆功能

function * foo() {
	console.log(1);
  yield 'Hello';
  console.log(2);
  yield 'world';
  console.log(3);
  return '!!!';
}

const iter = foo();

console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());

return 会结束函数执行

function * test(){
	yield 'a';
  yield 'b';
  yield 'c';
  return 'd'
}
var iter = test();
console.log(iter);
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next()); // return 返回
console.log(iter.next());

yield 本身不产出值

function * test() {
	var a = yield 'a';
  console.log('a==>>', a);
  return 'd'
}
var iter = test();
console.log(iter);
console.log(iter.next());
console.log(iter.next());

yield 的值可以通过 next 函数指定

function * test(){
	var a = yield 'a';
  console.log('a==>>', a);
  return 'd'
}
var iter = test();
console.log(iter);
console.log(iter.next(10));
console.log(iter.next(20));

yield 在表达式中,要用括号括起来充当表达式

function * demo() {
	console.log('hello' + (yield 123));
}

yield 作为参数

function * demo(){
	foo(yield 'a', yield 'b');
}
function foo(a, b){
	console.log(a, b);
}
var iter = demo();
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
function * demo(){
	foo(yield 'a', yield 'b');
}
function foo(a, b){
	console.log('foo--', a, b);
}
var iter = demo();
console.log(iter.next());
console.log(iter.next(1));
console.log(iter.next(2));

yield 产生迭代器对象,可以迭代, 不会包含 return 的值

function * foo(){
	yield 1;
	yield 2;
	yield 3;
	yield 4;
  return 5;
}
for(let i of foo()){
	console.log(i); // 不会打印出return的5
}

yield 产出值, next() 方法的参数表示上一个 yield 表达式的返回值

  • 在第一次使用next()方法时,传递参数是无效的。
  • 从语义上讲,第一个next()方法用来启动遍历器对象,所以不用带有参数。
  • next()方法的参数表示上一个yield表达式的返回值
function * foo(){
	let v1 = yield 1;
  console.log('v1-', v1);
	let v2 = yield 2;
  console.log('v2-', v2);
	let v3 = yield 3;
  console.log('v3-', v3);
	let v4 = yield 4;
  console.log('v4-', v4);
}
var iter = foo();
console.log(iter.next('A'));
console.log(iter.next('B'));
console.log(iter.next('C'));
console.log(iter.next('D'));
console.log(iter.next('E'));
console.log(iter.next('F'));

由于next()方法的参数表示上一个yield表达式的返回值,所以在第一次使用next()方法时,传递参数是无效的。V8 引擎直接忽略第一次使用next()方法时的参数,只有从第二次使用next()方法开始,参数才是有效的。从语义上讲,第一个next()方法用来启动遍历器对象,所以不用带有参数。

优化 iterator.next 代码

var obj = {
	start: [1,2,3],
  end: [7,8,9],
  [Symbol.iterator]: function * (){
  	var nextIndex = 0,
        arr = [...this.start, ...this.end];
    while(nextIndex < arr.length){
    	yield arr[nextIndex ++]
    }
  }
}
for(let i of obj){
	console.log(i);
}

generator 配合异步使用

promise

let fs = require('fs')
function promisify(fn){
    return function(...args){
        return new Promise((resolve,reject)=>{
            fn(...args,(err,data)=>{
                if(data){
                    resolve(data)
                }else{
                    reject(err)
                }
            })
        })
    }
}    
let readFile = promisify(fs.readFile);
readFile('./name.txt', 'utf-8')
	.then(res => readFile(res, 'utf-8'))
	.then(res => readFile(res, 'utf-8'))
	.then(res => console.log(res))

generator

let fs = require('fs')
function promisify(fn){
    return function(...args){
        return new Promise((resolve,reject)=>{
            fn(...args,(err,data)=>{
                if(data){
                    resolve(data)
                }else{
                    reject(err)
                }
            })
        })
    }
}    
var readFile = promisify(fs.readFile);
function * read(){
	let value1 = yield readFile('./name.txt', 'utf-8')
	let value2 = yield readFile(value1, 'utf-8')
	let value3 = yield readFile(value2, 'utf-8')
  console.log(value3)
}
var iter = read();
let {value, done} = iter.next();
value.then(res => {
	console.log(res)
  let {value, done} = iter.next(res); // res 会赋值给value1
  value.then(val => {
  	let {value, done} = iter.next(val); // val 会赋值给value2
    value.then(val3 => {
  		console.log(val3);
  	})
  })
})

co 提纯函数

let fs = require('fs')
function promisify(fn){
    return function(...args){
        return new Promise((resolve,reject)=>{
            fn(...args,(err,data)=>{
                if(data){
                    resolve(data)
                }else{
                    reject(err)
                }
            })
        })
    }
}    
var readFile = promisify(fs.readFile);
function * read(){
	let value1 = yield readFile('./name.txt', 'utf-8')
	let value2 = yield readFile(value1, 'utf-8')
	let value3 = yield readFile(value2, 'utf-8')
  console.log(value3)
}
function Co(iter){
	return new Promise((resolve, reject)=>{
  	let next = (data) => {
    	let {value, done} = iter.next(data);
      if(done){
      	resolve(value)
      }else{
      	value.then((val)=>{
        	next(val)
        })
      }
    }
    next();
  })
}

let promise = Co(read())
promise.then(val => console.log(val))
const fs = require('fs');

const promisify = (fn) => {
  return function(...args) {
    return new Promise((resolve, reject) => {
      fn(...args, (err, data) => {
        if (data) {
          resolve(data)
        } else {
          reject(err)
        }
      })
    });
  }
}

const readFile = promisify(fs.readFile);

function * read() {
  const value1 = yield readFile('./first.txt', 'utf-8');
  const value2 = yield readFile(value1, 'utf-8');
  const value3 = yield readFile(value2, 'utf-8');
  return value3;
}

function Co(iter) {
  return new Promise((resolve, reject) => {
    const next = (data) => {
      const { value, done } = iter.next(data);
      if (done) {
        resolve(value);
      } else {
        value.then(val => next(val))
      }
    }
    next();
  })
}

const p = Co(read());
p.then(val => console.log(val));

return

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

var g = gen();

console.log(g);

return 会终止产出

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

var g = gen();

console.log(g.next());
console.log(g.next());
console.log(g.return());
console.log(g.next());
console.log(g.next());

return() 传值,结果和在 generator 中直接 return 效果一样

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

var g = gen();

console.log(g.next());
console.log(g.next());
console.log(g.return(10));
console.log(g.next());
console.log(g.next());
function * gen() {
	yield 1;
  yield 2;
  return 10;
  yield 3;
}

var g = gen();

console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());

throw

try-catch 无法捕获异步异常

var g = function * (){
	try{
  	yield;
  }catch(e){
  	console.log('生成器异常:', e)
  }
  console.log(1);
}
var i = g();
console.log(i.throw('a'))
var g = function * (){
	try{
  	yield;
  }catch(e){
  	console.log('生成器异常:', e)
  }
  console.log(1);
}
var i = g();
console.log(i.next())
console.log(i.throw('a'))

throw 还会等同于 next 操作,会执行一次 yield

var g = function * (){
  yield 1;
	try{
  	yield 2;
  }catch(e){
  	console.log('生成器异常:', e)
  }
  yield 3;
}
var i = g();
console.log(i.next())
console.log(i.next())
console.log(i.throw('a')) // 相当于执行了第三次next
console.log(i.next())

在生成器函数中 try catch 是可以捕获到异步异常的,普通函数中是不可以得

const fs = require('fs');
const util = require('util');
const co = require('co');

const readFile = util.promisify(fs.readFile);

function * read(){
  try {
    let value1 = yield readFile('./first.tx','utf-8');
    let value2 = yield readFile(value1,'utf-8');
    let value3 = yield readFile(value2,'utf-8');
    return value3;
  } catch (error) {
    console.log('15'+error)
  }
  console.log('hello world');
}

const p = co(read());
p.then(val => console.log(val))
最后更新于 2023-07-20