什么是迭代器?
一个简单的列子
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);
});
复制代码
参考链接: