Generator
生成器是一种返回迭代器对象的函数。
形式上,Generator 函数是一个普通函数,但是有两个特征
- function关键字与函数名之间有一个星号;
 - 函数体内部使用yield表达式,定义不同的内部状态
 - yield 表达式后面的表达式,只有当调用 next 方法、内部指针指向该语句时才会执行
 - yield 关键字实际返回一个 Iterator 对象,它有两个属性,
value和done - yield 表达式 用于委托给另一个 generator 或可迭代对象。 ```javascript function helloWorldGenerator() { yield ‘hello’; yield ‘world’; return ‘ending’; }
 
var hw = helloWorldGenerator();
hw.next()// { value: ‘hello’, done: false }
hw.next()// { value: ‘world’, done: false }
hw.next()// { value: ‘ending’, done: true }
hw.next()// { value: undefined, done: true }
由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的 Symbol.iterator 属性,从而使得该对象具有 Iterator 接口。```javascriptvar myIterable = {};myIterable[Symbol.iterator] = function* () {yield 1;yield 2;yield 3;};[...myIterable] // [1, 2, 3]
Generator 函数执行后,返回一个遍历器对象。该对象本身也具有 Symbol.iterator 属性,执行后返回自身。
function* gen(){// some code}var g = gen();g[Symbol.iterator]() === g// true
for…of 循环可以自动遍历 Generator 函数运行时生成的 Iterator对象,且此时不再需要调用 next方法。
function* foo() {yield 1;yield 2;yield 3;yield 4;yield 5;return 6;}for (let v of foo()) {console.log(v); // 1 2 3 4 5}
虽然 Generator 函数将异步操作表示得很简洁,但是流程管理却不方便
Thunk函数
Thunk 函数是自动执行 Generator 函数的一种方法。
const Thunk = function(fn) {return function (...args) {return function (callback) {return fn.call(this, ...args, callback);}};};
// 生成fs.readFile的 Thunk 函数var readFileThunk = Thunk(fs.readFile);readFileThunk(fileA)(callback);
基于 Thunk 函数的 Generator 执行器
function run(fn) {var gen = fn();function next(err, data) {var result = gen.next(data);if (result.done) return;result.value(next);}next();}// 跟在yield命令后面的必须是 Thunk 函数function* g() {var f1 = yield readFileThunk('fileA');var f2 = yield readFileThunk('fileB');// ...var fn = yield readFileThunk('fileN');}run(g);
co 模块
自执行器
function run(gen){var g = gen();function next(data){var result = g.next(data);if (result.done) return result.value;result.value.then(function(data){next(data);});}next();}run(gen);
async
async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。
async 函数内部 return 语句返回的值,会成为 then 方法回调函数的参数。
async 函数内部抛出错误,会导致返回的 Promise 对象变为 reject 状态。抛出的错误对象会被 catch 方法回调函数接收到。
async function f() {throw new Error('出错了');}f().then(v => console.log('resolve', v),e => console.log('reject', e))//reject Error: 出错了
async 函数返回的 Promise 对象,必须等到内部所有 await 命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到 return 语句或者抛出错误。也就是说,只有 async 函数内部的异步操作执行完,才会执行 then 方法指定的回调函数。
async 函数的实现原理
async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。
async function fn(args) {// ...}// 等同于function fn(args) {return spawn(function* () {// ...});}
其中的 spawn 函数就是自动执行器,spawn 函数的实现,基本就是前文自动执行器的翻版。
function spawn(genF) {return new Promise(function(resolve, reject) {const gen = genF();function step(nextF) {let next;try {next = nextF();} catch(e) {return reject(e);}if(next.done) {return resolve(next.value);}Promise.resolve(next.value).then(function(v) {step(function() { return gen.next(v); });}, function(e) {step(function() { return gen.throw(e); });});}step(function() { return gen.next(undefined); });});}
await
await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,则会执行Promise.resolve()进行转换。
awai t命令后面的 Promise对象,运行结果可能是 rejected,所以最好把 await 命令放在 try…catch 代码块中。
async function myFunction() {try {await somethingThatReturnsAPromise();} catch (err) {console.log(err);}}// 另一种写法async function myFunction() {await somethingThatReturnsAPromise().catch(function (err) {console.log(err);});}
多个 await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。
let foo = await getFoo();let bar = await getBar();// 写法一let [foo, bar] = await Promise.all([getFoo(), getBar()]);// 写法二let fooPromise = getFoo();let barPromise = getBar();// 先执行后异步看结果let foo = await fooPromise;let bar = await barPromise;
forEach问题
function dbFuc(db) { //这里不需要 asynclet docs = [{}, {}, {}];// 可能得到错误结果docs.forEach(async function (doc) {await db.post(doc);});}
// 原理Array.prototype.forEach = function (callback) {for (let index = 0; index < this.length; index++) {callback(this[index], index, this);}};// callback前面并没有使用await,所以是同步执行的
正确的写法是采用 for..of 循环
async function dbFuc(db) {let docs = [{}, {}, {}];//继发执行for (let doc of docs) {await db.post(doc);}
如果确实希望多个请求并发执行,可以使用 Promise.all 方法。当三个请求都会 resolved 时返回
async function dbFuc(db) {let docs = [{}, {}, {}];let promises = docs.map((doc) => db.post(doc));let results = await Promise.all(promises);console.log(results);}
