学习链接
异步操作-Generator
Promise 的最大问题是代码冗余,原来的任务被 Promise 包装了一下,不管什么操作,一眼看去都是一堆 then,原来的语义变得很不清楚。
异步编程(多任务)的一种解决方案——“协程”(coroutine),意思是多个线程互相协作,完成异步任务。
协程有点像函数,又有点像线程。它的运行流程大致如下。
- 第一步,协程
A开始执行。 - 第二步,协程
A执行到一半,进入暂停,执行权转移到协程B。 - 第三步,(一段时间后)协程
B交还执行权。 - 第四步,协程
A恢复执行。
上面流程的协程 A,就是异步任务,因为它分成两段(或多段)执行。
Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。
虽然 Generator 函数将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)。
Thunk 函数
“求值策略”,即函数的参数到底应该何时求值。
var x = 1;function f(m) {return m * 2;}f(x + 5)
- 传值调用:即在进入函数体之前,就计算
x + 5的值(等于 6),再将这个值传入函数f。f(x + 5) => f(6)
- 传名调用:即直接将表达式
x + 5传入函数体,只在用到它的时候求值。f(x + 5) => (x + 5) * 2
编译器的“传名调用”实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫做 Thunk 函数。
Thunk 函数是“传名调用”的一种实现策略,用来替换某个表达式。
可用于自动执行 Generator 函数。
function thunkify(fn) {return function (...args) {const ctx = this;return function (done) {let called;args.push(function () {if (called) return;called = true;done.apply(null, arguments);});try {fn.apply(ctx, args);} catch (err) {done(err);}}}};
手动流程管理
const readFileThunk = thunkify(fs.readFile);const gen = function* (){var r1 = yield readFileThunk('fileA');console.log(r1.toString());var r2 = yield readFileThunk('fileB');console.log(r2.toString());};const g = gen();let r1 = g.next();r1.value(function (err, data) {if (err) throw err;let r2 = g.next(data);r2.value(function (err, data) {if (err) throw err;g.next(data);});});
自动流程管理
const readFileThunk = thunkify(fs.readFile);function run(fn) {const gen = fn();function next(err, data) {const result = gen.next(data);if (result.done) return;result.value(next);}next();}function* g() {let f1 = yield readFileThunk('fileA');let f2 = yield readFileThunk('fileB');// ...let fn = yield readFileThunk('fileN');}run(g);
Thunk 函数并不是 Generator 函数自动执行的唯一方案。
因为自动执行的关键是,必须有一种机制,自动控制 Generator 函数的流程,接收和交还程序的执行权。回调函数可以做到这一点,Promise 对象也可以做到这一点。
co 模块
基于 Thunk 和 Promise。
// 接受 Generator 函数作为参数,返回 Promise 对象function co(gen) {const ctx = this;return new Promise(function(resolve, reject) {// 先检查参数gen是否为 Generator 函数// 如果是,则执行该函数 得到一内部指针对象if (typeof gen === 'function') gen = gen.call(ctx);// 如果不是,则返回 Promised 对象if (!gen || typeof gen.next !== 'function') return resolve(gen);onFulfilled();// 将 Generator 函数的内部指针对象的 next 方法包装成 onFulfilled 函数function onFulfilled(res) {let ret;try {ret = gen.next(res);} catch (e) {return reject(e);}next(ret);}});}function next(ret) {// 当前是否为 Generator 函数的最后一步if (ret.done) {return resolve(ret.value);}// 确保每一步的返回值都是 Promise 对象let value = toPromise.call(ctx, ret.value);// 使用then方法,为返回值加上回调函数,然后通过onFulfilled函数再次调用next函数if (value && isPromise(value)) {return value.then(onFulfilled, onRejected);}// 在参数不符合要求的情况下(参数非 Thunk 函数和 Promise 对象)// 将 Promise 对象的状态改为rejected,从而终止执行return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '+ 'but the following object was passed: "'+ String(ret.value)+ '"'));}
Generator 与协程

Generator 与上下文

语法
语法简介

// 惰性求值function* gen() {yield 123 + 456;}// 暂缓执行function* f() {console.log('执行了!')}var generator = f();setTimeout(function () {generator.next()}, 2000);
var arr = [1, [[2, 3], 4], [5, 6]];var flat = function* (a) {a.forEach(function (item) {if (typeof item !== 'number') {yield* flat(item); // Uncaught SyntaxError: Unexpected identifier} else {yield item; // Uncaught SyntaxError: Unexpected identifier}});};for (var f of flat(arr)){console.log(f);}
function* demo() {console.log('Hello' + yield); // SyntaxErrorconsole.log('Hello' + yield 123); // SyntaxErrorconsole.log('Hello' + (yield)); // OKconsole.log('Hello' + (yield 123)); // OK}
function* demo() {foo(yield 'a', yield 'b'); // OKlet input = yield; // OK}function* gen(x){var y = yield x + 2;// 等价于 yield (x + 2)return y;}var g = gen(1);g.next() // { value: 3, done: false }g.next(2) // { value: 2, done: true }
function* gen(){// some code}var g = gen();g[Symbol.iterator]() === g// true
next 方法的参数

// 注意:执行了 yield 表达式后就暂停了, 表达式并未返回值// yield 表达式的返回值由下一次的 next 的参数决定// 故第二次调用 next 才输出 1.afunction* dataConsumer() {console.log('Started');console.log('1. ' + yield);console.log('2. ' + yield);return 'result';}let genObj = dataConsumer();genObj.next();// StartedgenObj.next('a')// 1. agenObj.next('b')// 2. b
for…of 循环

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
// 不修改原生对象,捏造一个新接口function* objectEntries(obj) {let propKeys = Reflect.ownKeys(obj);for (let propKey of propKeys) {yield [propKey, obj[propKey]];}}let jane = { first: 'Jane', last: 'Doe' };for (let [key, value] of objectEntries(jane)) {console.log(`${key}: ${value}`);}// first: Jane// last: Doe
// 直接给对象 Symbol.iterator 赋值function* objectEntries() {let propKeys = Object.keys(this);for (let propKey of propKeys) {yield [propKey, this[propKey]];}}let jane = { first: 'Jane', last: 'Doe' };jane[Symbol.iterator] = objectEntries;for (let [key, value] of jane) {console.log(`${key}: ${value}`);}// first: Jane// last: Doe
function* numbers () {yield 1;yield 2;return 3;yield 4;}// 扩展运算符[...numbers()] // [1, 2]// Array.from 方法Array.from(numbers()) // [1, 2]// 解构赋值let [x, y] = numbers();x // 1y // 2// for...of 循环for (let n of numbers()) {console.log(n)}// 1// 2
Generator.prototype.throw()

var g = function* () {try {yield;} catch (e) {console.log('内部捕获', e);}};var i = g();i.next();try {i.throw('a');i.throw('b'); // 内部 catch 过了,传到外部} catch (e) {console.log('外部捕获', e);}// 内部捕获 a// 外部捕获 b
var gen = function* gen(){yield console.log('hello');yield console.log('world');}var g = gen();g.next();try {throw new Error();} catch (e) {g.next();}// hello// world
function* gen() {
try {
yield 1;
} catch (e) {
console.log('内部捕获');
}
}
var g = gen();
g.throw(1);
// Uncaught 1
var gen = function* gen(){
try {
yield console.log('a');
} catch (e) {
// ...
}
yield console.log('b');
yield console.log('c');
}
var g = gen();
g.next() // a
g.throw() // b
g.next() // c
Generator.prototype.return()

function* gen() {
yield 1;
console.log(123)
yield 2;
yield 3;
}
var g = gen();
g.next() // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next() // { value: undefined, done: true }
g.return('foo 123') // { value: "foo 123", done: true }
function* numbers () {
yield 1;
try {
yield 2;
yield 3;
} finally {
yield 4;
yield 5;
}
yield 6;
}
var g = numbers();
g.next() // { value: 1, done: false }
g.next() // { value: 2, done: false }
g.return(7) // { value: 4, done: false }
g.return(8) // { value: 8, done: true }
g.next() // { value: undefined, done: true }
g.next() // { value: undefined, done: true }
function* numbers () {
yield 1;
try {
yield 2;
yield 3;
} finally {
yield 4;
yield 5;
}
yield 6;
}
var g = numbers();
g.next() // { value: 1, done: false }
g.next() // { value: 2, done: false }
g.return(7) // { value: 4, done: false }
g.next() // { value: 5, done: false }
g.next() // { value: 7, done: true }
next()、throw()、return() 的共同点

const g = function* (x, y) {
let result = yield x + y;
return result;
};
const gen = g(1, 2);
gen.next(); // Object {value: 3, done: false}
gen.next(1); // Object {value: 1, done: true}
// 相当于将 let result = yield x + y
// 替换成 let result = 1;
const g = function* (x, y) {
let result = yield x + y;
return result;
};
const gen = g(1, 2);
gen.next(); // Object {value: 3, done: false}
gen.throw(new Error('出错了')); // Uncaught Error: 出错了
// 相当于将 let result = yield x + y
// 替换成 let result = throw(new Error('出错了'));
const g = function* (x, y) {
let result = yield x + y;
return result;
};
const gen = g(1, 2);
gen.next(); // Object {value: 3, done: false}
gen.return(2); // Object {value: 2, done: true}
// 相当于将 let result = yield x + y
// 替换成 let result = return 2;
yield* 表达式

function* foo() {
yield 'a';
yield 'b';
}
function* bar() {
yield 'x';
yield* foo();
yield 'y';
}
// 等同于
function* bar() {
yield 'x';
yield 'a';
yield 'b';
yield 'y';
}
// 等同于
function* bar() {
yield 'x';
for (let v of foo()) {
yield v;
}
yield 'y';
}
for (let v of bar()){
console.log(v);
}
// "x"
// "a"
// "b"
// "y"
function* foo() {
yield 2;
yield 3;
return "foo";
}
function* bar() {
yield 1;
var v = yield* foo();
console.log("v: " + v);
yield 4;
}
var it = bar();
it.next()
// {value: 1, done: false}
it.next()
// {value: 2, done: false}
it.next()
// {value: 3, done: false}
it.next();
// "v: foo"
// {value: 4, done: false}
it.next()
// {value: undefined, done: true}
function* iterTree(tree) {
if (Array.isArray(tree)) {
for(let i = 0; i < tree.length; i++) {
yield* iterTree(tree[i]);
}
} else {
yield tree;
}
}
const tree = [ 'a', ['b', 'c'], ['d', 'e'] ];
[...iterTree(tree)] // ["a", "b", "c", "d", "e"]
for(let x of iterTree(tree)) {
console.log(x);
}
// a
// b
// c
// d
// e
Generator 函数的 this

function* g() {}
g.prototype.hello = function () {
return 'hi!';
};
let obj = g();
obj instanceof g // true
obj.hello() // 'hi!'
function* g() {
this.a = 11;
}
let obj = g();
obj.next();
obj.a // undefined
function* F() {
this.a = 1;
yield this.b = 2;
yield this.c = 3;
}
var obj = {};
var f = F.call(obj); // 即让 this 指向 obj
f.next(); // Object {value: 2, done: false}
f.next(); // Object {value: 3, done: false}
f.next(); // Object {value: undefined, done: true}
obj.a // 1
obj.b // 2
obj.c // 3
function* gen() {
this.a = 1;
yield this.b = 2;
yield this.c = 3;
}
function F() {
return gen.call(gen.prototype); // this 指向原型
}
var f = new F();
f.next(); // Object {value: 2, done: false}
f.next(); // Object {value: 3, done: false}
f.next(); // Object {value: undefined, done: true}
f.a // 1
f.b // 2
f.c // 3
