[FE] Yield a Continuation ...
1. 从 Generator 说起
ecma-262 2015 引入了 yield,它被用在了 function*
中。
用法如下:
function* gen() {
yield 1;
yield 2;
return 3;
}
const iter = gen(); // iter = generator object = (iterator/iterable)
console.log(iter.next()); // { value: 1, done: false }
console.log(iter.next()); // { value: 2, done: false }
console.log(iter.next()); // { value: 3, done: true }
其中,function* gen() { ... }
称为 generator function,返回值称为 generator object。
generator object 既是一个 iterator 又是一个 iterable。
2. iterator/iterable
(1)什么是 iterator
An object is an iterator when it implements a
next()
method that returns an object with at least the following two properties:
- done
Has the valuefalse
if the iterator was able to produce the next value in the sequence. (This is equivalent to not specifying thedone
property altogether.)
Has the valuetrue
if the iterator has completed its sequence. In this case,value
optionally specifies the return value of the iterator. - value
Any JavaScript value returned by the iterator. Can be omitted when done istrue
.
即,iterator 是一个实现了 next()
方法的对象,该方法返回一个 {done, value}
。
上面的例子反映了 generator object 确实是一个 iterator。
function* gen() {
yield 1;
yield 2;
return 3;
}
const iter = gen();
iter.next() // {value: 1, done: false}
(2)什么是 iterable
iterable 指的是满足 iterable protocol 的对象。
In order to be iterable, an object must implement the
@@iterator
method, meaning that the object (or one of the objects up its prototype chain) must have a property with a@@iterator
key which is available via constantSymbol.iterator
Property | Value |
---|---|
[Symbol.iterator] |
A zero-argument function that returns an object, conforming to the iterator protocol. |
Whenever an object needs to be iterated (such as at the beginning of a
for...of
loop), its@@iterator
method is called with no arguments, and the returned iterator is used to obtain the values to be iterated.
即,iterable 是一个实现了 Symbol.iterator
方法的对象,该方法返回一个 iterator。
我们来看 generator object 是不是一个 iterable。
function* gen() {
yield 1;
yield 2;
return 3;
}
const iter = gen();
const iterator = iter[Symbol.iterator]();
iterator.next(); // {value: 1, done: false}
并且,generator object 当做 iterable 时,Symbol.iterator
方法返回的 iterator 刚好是它自己。
const iter = gen();
const iterator = iter[Symbol.iterator]();
iter === iterator; // true
(3)小结
generator object 大概是这样的一个对象,
const iter = {
// iterator protocol
next: (x) => ({ value: xxx, done: xxx }),
// iterable protocol
[Symbol.iterator]: () => iter
};
iter[Symbol.iterator]() === iter
3. yield*
generator function 中除了可以使用 yield 之外,还可以使用 yield*。
跟 yield 不同的是,yield* 可以用来 delegate 其它的 iterable。
何为 delegate 呢?
function* gen1() {
yield 3;
yield 4;
}
function* gen2() {
yield* [1, 2];
yield* "ab";
yield* gen1();
}
const iter = gen2();
console.log(iter.next()); // { value: 1, done: false }
console.log(iter.next()); // { value: 2, done: false }
console.log(iter.next()); // { value: "a", done: false }
console.log(iter.next()); // { value: "b", done: false }
console.log(iter.next()); // { value: 3, done: false }
console.log(iter.next()); // { value: 4, done: false }
console.log(iter.next()); // { value: undefined, done: true }
即,yield* 可以 “钻” 到 iterable 里面去 yield。
4. koa compose
koa v1.7.0 使用了 co v4.6.0 + koa-compose v2.5.1 来处理 middleware。
$ mkdir test-koa && cd test-koa
$ npm i -S koa
const Koa = require('koa');
const app = new Koa();
// logger
app.use(function *(next){
const start = new Date;
yield next;
const ms = new Date - start;
console.log('%s %s - %s', this.method, this.url, ms);
});
// response
app.use(function *(){
this.body = 'Hello World';
});
app.listen(3000);
5. Continuation
(1)什么是 Continuation
(2)什么是 First-class Continuation
Wikipedia: Continuation#first-class_continuation
TSPL 4th Ch3.3
(+ 0 (call/cc
(lambda (k)
(k 1))))
=> 1
(+ 0 (call/cc
(lambda (k)
2)))
=> 2
(let ([x (call/cc (lambda (k) k))])
(x (lambda (ignore) “hi”)))
=> “hi”
(3)yield 一个 Continuation
yieldContinuation(function* () {
const v1 = yield function (cont) {
setTimeout(function () {
cont('Hello');
}, 1000);
};
console.warn(v1);
const v2 = yield function (cont) {
setTimeout(function () {
cont('World');
}, 500);
};
console.warn(v2);
});
6. 阴阳谜题
function* solution() {
function* yin(_yin) {
yield '@';
function* yang(_yang) {
yield '*';
yield* _yin(_yang);
}
yield* yang(yang);
}
yield* yin(yin);
}
const iter = solution();
for(const i of iter) {
console.log(i); // @*@**@***@****...
}
(let* [(yin ((lambda (foo) (newline) foo)
(call/cc (lambda (bar) bar))))
(yang ((lambda (foo) (display "*") foo)
(call/cc (lambda (bar) bar))))]
(yin yang))
7. Delimited Continuation
(1)什么是 Delimited Continuation
Wikipedia: delimited continuation
Continuations, coroutines, fibers, effects
(* 2
(reset
(+ 1
(shift k (k 5))
)
)
)
=>
(* 2
(+ 1 5)
)
(2)Yield 一个 Delimited Continuation
Algebraic Effects in JavaScript part 3 - Delimited Continuations
const result = yieldDelimitedContinuation(function* (reset) {
const a1 = yield 1;
// 使用 reset 定界
const a2 = yield reset(function* (shift) {
const b1 = yield 2;
const b2 = yield function* () {
const c1 = yield 3;
// 使用 shift 返回到 reset 位置
const c2 = yield shift(function* () {
const u = yield 4;
const v = yield 5;
// 4 + 5
// 直接return到reset的位置 a2 = 9,然后从a2处开始执行
return u + v;
});
const c3 = yield 6;
return c1 + c2 + c3;
};
const b3 = yield 7;
return b1 + b2 + b3;
});
const a3 = yield 8;
// 1 + 9 + 8
return a1 + a2 + a3;
});
console.log(result); // 18
源码:yield-delimited-continuation
8. Algebraic Effect
至于 Algebraic Effect?恕我智商太低,实在跟不上你们的学习进度。
Algebraic operations are, in the sense we shall make precise, a natural generalisation, from Set to an arbitrary symmetric monoidal V-category C with cotensors, of the usual operations of universal algebra, taking T to be a strong V-monad on C. —— Algebraic Operations and Generic Effects
参考
MDN: yield
MDN: generator function
MDN: generator object
MDN: iterator
MDN: iterable
MDN: yield*
github: koa v1.7.0
github: co v4.6.0
github: koa-compose v2.5.1
Wikipedia: Continuation
Wikipedia: Continuation#first-class_continuation
Wikipedia: delimited continuation
Continuations, coroutines, fibers, effects
Algebraic Effects in JavaScript part 3 - Delimited Continuations
TSPL 4th Ch3.3
Algebraic Operations and Generic Effects