2.7.JavaScript异步流程控制-generator+co

202 阅读3分钟
  • generator(生成器),由es6提供的,生成的是迭代器;
  • 进阶的语法:async + await;
  • dva + react 中有redux-saga,redux-saga根据generator写出来的;
  • generator 可以配合promise使用,也可以不配合使用
  • 生成器是用来生成迭代器的;

generotor.1.js

let likeArray = { 0: 1, 1: 2, 2: 3, length: 3 };
let arr = [...likeArray];
console.log(arr); // 报错:likeArray is not iterable

generotor.2.js

// 函数对象内置:Sync
function arg() {
  console.log([...arguments]); // 打印:[ 1, 2, 3, 4 ]
}
arg(1, 2, 3, 4);

generotor.3.js

  • 迭代器就是一个有next方法的对象,每次调用next都会返回一个对象,对象里有done,value;for of必须拥有迭代器的方法才能调用;
// 对象添加迭代器
let likeArray = {
  0: 1, 1: 2, 2: 3, length: 3, [Symbol.iterator]() {
    let flag = false; // 默认迭代未完成
    let index = 0; // 当前对象的索引
    let that = this; // 当前对象
    return {
      next() {
        return { done: index === that.length, value: that[index++] }; // donetrue表示迭代完成,为false迭代未完成会一值迭代
      }
    }
  }
};
// 迭代器就是一个有next方法的对象,每次调用next都会返回一个对象,对象里有done,value;for of必须拥有迭代器的方法才能调用;
let arr = [...likeArray];
console.log(arr); // 打印:[ 1, 2, 3 ]

generotor.4.js

  • 生成器可以实现生成迭代器,生成器函数就是再函数关键字中加个*配合yield来使用
let likeArray = {
  0: 1, 1: 2, 2: 3, length: 3, [Symbol.iterator]: function* () {
    let index = 0;
    yield this[index++]; 
    yield this[index++];
  }
};
let arr = [...likeArray];
console.log(arr); // 打印:[ 1, 2]

generator.5.js

  • yield方法原理:执行next方法时,每次遇到yield方法会停止执行函数,并返回yield后的值;
  • yield函数
function * gen(){
  yield 1;
  yield 2;
}
let it = gen();
let r = it.next();
console.log(r);
r = it.next();
console.log(r);
r = it.next();
console.log(r);
/* 
打印:
{ value: 1, done: false }
{ value: 2, done: false }
{ value: undefined, done: true } 
*/

generator.6.js

  • generator的好处就是遇到yield就会暂停,调用next会继续往下执行
  • generator原理
function * gen(){
  yield 1;
  yield 2;
}
let it = gen();
let flag = false;
do{
  let {value,done} = it.next();
  flag = done;
  console.log(value); // 打印: 1 2 undefined
}while(!flag);

generator.7.js

let likeArray = {
  0: 1, 1: 2, 2: 3, length: 3, [Symbol.iterator]: function* () {
    let index = 0;
    while (index !== this.length) {
      yield this[index++];
    }
  }
};
let arr = [...likeArray];
console.log(arr); // 打印:[ 1, 2, 3 ]

generator.8.js

  • generator next参数使用原理:第一次传参无意义;第二次及以上传参,参数会返回给上次yield的返回值;
function* say() {
  let a, b, c;
  a = yield 'hello';
  console.log('a', a);
  b = yield 'world';
  console.log('b', b);
  c = yield 'Kong';
  console.log('c', c);
}
let it = say();
it.next(100); // 第一次next传递参数是无意义的
it.next(200); // 第二次next执行时传递的参数会返回给第一次yield的返回值;
it.next(300);
it.next(400);
/*
打印:
a 200
b 300
c 400
*/

generator.9.js

  • 多文件关联读取信息
let fs = require('fs');
function read(file) {
  return new Promise(function (resolve, reject) {
    fs.readFile(file, 'utf8', function (err, data) {
      if (err) reject(err);
      resolve(data);
    })
  })
}
function* r() {
  let r1 = yield read('name.txt');
  let r2 = yield read(r1);
  let r3 = yield read(r2);
  return r3;
}
let it = r();
let { value, done } = it.next();
// generator方法
value.then(function (data) { // data -> age.txt
  let { value, done } = it.next(data);
  value.then(function (data) { // data -> address.txt
    let { value, done } = it.next(data);
    value.then(function (data) { // data -> 结果
      console.log(data);
    })
  })
})
// promise方法
read('name.txt', function (err, data) {
  return read(data);
}).then(function (data) {
  return read(data);
}).then(function (data) {
  return read(data);
}).then(function (data) {
  console.log(data);
})

generator.10.js

  • generator+co完整版
  • 可以引用co库 cnpm i co
  • 自定义co函数
let fs = require('fs');
function read(file) {
  return new Promise(function (resolve, reject) {
    fs.readFile(file, 'utf8', function (err, data) {
      if (err) reject(err);
      resolve(data);
    })
  })
}
function* r() {
  let r1 = yield read('pracetice/name.txt');
  let r2 = yield read(r1);
  let r3 = yield read(r2);
  return r3;
}
function co(it) {
  return new Promise(function (resolve, reject) {
    // next方法 express koa 原理都是这样的
    function next(data) {
      let { value, done } = it.next(data); // 首次执行无意义
      if (done) {
        resolve(value);
      } else {
        value.then(function (data) {
          next(data);
        }, reject)
      }
    }
    next();
  })
}
co(r()).then(function (data) {
  console.log(data);
})