什么是迭代器?
一个简单的列子
function makeIterator (arr) { let nextIndex = 0; //返回一个迭代器方法 return { next: () => { if(nextIndex < arr.length){ return { value: arr[nextIndex++], done: false} }else{ return { done: true} } } }}const it = makeIterator([1,2,3]);console.log('1:', it.next());console.log('2:', it.next());console.log('3:', it.next());console.log('end:', it.next());复制代码
执行结果:
1: {value: 1, done: false}2: {value: 2, done: false}3: {value: 3, done: false}end: { done: true}复制代码
上述为一个简单的迭代器,因为迭代器的代码过于繁琐,产生了生成器即Generator
什么是生成器?
较为简单的理解,生成器会返回一个迭代器,简化了代码,下面为一个生成器的列子。
function *makeIterator (arr) { for(let i = 0; i < arr.length; i++){ yield arr[i]; }}const gen = makeIterator(['a','b','c']);console.log('1', gen.next());console.log('2', gen.next());console.log('3', gen.next());console.log('end', gen.next());复制代码
执行结果和上述相同:
1: {value: 1, done: false}2: {value: 2, done: false}3: {value: 3, done: false}end: { done: true}复制代码
有了上述关于迭代器和生成器的概念,我们来看下关于js的异步操作。
文件目录结构如下async│ └───content.js└───asyncRead.js复制代码
content.js:
const content = '此js为测试js';复制代码
- 普通的js回调
const fs = require('fs');function readFile(cb){ fs.readFile('./content.js', (err, data) => { if(err) return cb(err); cb && cb(null,data); })}//1. 回调函数readFile((err, data) => { if(!err){ //返回的是buffer类型 console.log('data:', data); //返回正常内容 console.log('data.toString():', data.toString); }else{ console.log('err:', err); }})复制代码
- promise
const fs = require(fs);function readFileAsync(path) { //定义了一个执行器 return new Promise((resolve, reject) => { fs.readFile(path, (err, data) => { if(err) reject(err); else resolve(data); }) })}readFileAsync('./content.js') .then(data => { console.log('data:', data.toString()); }) .catch(err => { console.log('err:', err); })复制代码
- co + Generator + Promise 生成器的方法
const fs = require(fs);const co = require('co');const util = require('util');const readFileFunc = util.promisify(fs.readFile);co(function *(){ let data = yield readFileFunc('./content.js'); data = data.toString(); console.log('data:', data);})复制代码
- async await (为Generator的语法糖,更加语义化)
const fs = require('fs');const util = require('util');const readAsync = util.promisify(fs.readFile);async function init() { let data = await readAsync('./content.js'); console.log('dataToString:', data.toString())}init()复制代码
关于生成器原理的一点补充:
先看一个简单的例子:
function *gen(x){ var y = yield x + 2; return y}var g = gen(1);console.log(g.next());console.log(g.next());复制代码
输出结果:
Object {value: 3, done: false}Object {value: undefined, done: true}复制代码
分析上述执行结果:
调用generator函数,会产生一个内部指针(遍历器)g,这是 Generator 函数不同于普通函数的另一个地方,即执行它不会返回结果,返回的是指针对象。调用指针 g 的 next 方法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的 yield 语句,上例是执行到 x + 2 为止。
换言之,next 方法的作用是分阶段执行 Generator 函数。每次调用 next 方法,会返回一个对象,表示当前阶段的信息( value 属性和 done 属性)。value 属性是 yield 语句后面表达式的值,表示当前阶段的值;done 属性是一个布尔值,表示 Generator 函数是否执行完毕,即是否还有下一个阶段。
稍微复杂一点的实例:
var fetch = require('node-fetch');//生成器function* gen(){ var url = 'https://api.github.com/users/github'; var result = yield fetch(url); console.log(result.bio);}//gen()函数生成一个内部指针var g = gen();//执行next()方法,执行到yield交出执行权var result = g.next();//拿到返回结果,以为fetch返回的是promise对象,后面进行promise处理result.value.then(function(data){ return data.json();}).then(function(data){ g.next(data);});复制代码
参考链接: