在 ES6 中引入的众多特性中,yield 无疑是很吸引笔者的。JavaScript 提供的 yield 首先让笔者联想到的是 Python 中提供的 yield。熟悉 Python 的同学,应该都知道,yield 可以用来做:包一个函数转变为 generator、可以使用 yield 实现 cooperative task。哪 JavaScript 中提供的 yield 可以用来做什么呢,后面会讲到,首先来看看 ES6 中 yield 的基本语法吧。
Yield Syntax
在使用 yield 时,通常是配合 generator function 使用,首先来看下 generator function 是怎么定义的,如下
1 | function *foo () { |
和普通的 JavaScript 中的函数,唯一的区别就是在函数的名字前面多了一个 *。接着就是这次的重点了,语法如下:
1 | yield expression // 不能是语句 |
配合起来就是这样的:
1 | function *foo () { |
调用 generator function 和 普通函数并没有什么不同,比如对于上面的 generator function 直接这样使用即可:
1 | var it = foo(); |
虽然执行了 foo() 但是,该函数并不会立即返回,而是这样:语句执行到 yield 时,首先计算 expression,如果其有返回值,就执行通过在执行 it.next() 时候返回,然后函数会在 yield 处挂起,等待下次调用 it.next() 时,函数再恢复执行,直到执行完整个函数体。
虽然上面 generator function 中,最后都没有写 return,但实际上在其函数体内也是可以使用 return 的:
1 | function *foo() { |
注:在配合 for ... of ... 使用时,最后 return 的值会被忽略掉。
再来看一个复杂的例子,有助于理解 yield:
1 | function *foo(x) { |
上面的例子中,在执行第一个 it.next 时,得到了 6,这是因为 5 + 1 = 6 然后将表达式的值,通过 yield 返回。这里执行的地一个 it.next 并没有提供任何参数,这是因为对于 generator function 第一个 next call 时,是因为这里还没有任何 yield 表达式,即使传递了参数,也会被忽略掉,并没有什么意义。
generator function 有下面两个方法:
- Yield Next
在上面的例子中,已经开到了 next 函数,该函数的作用主要是:获取 yield ___ 执行的表达式的值,并且唤醒正在等待了 yield,在恢复执行时,可以通过该函数传递 value。
- Yield Throw
可以通过这个函数,抛出异常 it.throw('xx') ,抛出的异常可以在 generator function 中被捕获,如果没有在 generator function 中被捕获,异常就会被传递到当前执行 it.throw('xx') 的语句处:
在 generator function 内部被捕获:
1 | function *foo() { |
不在 generator function 中被捕获:
1 | function *foo() { |
Yield Delegate
通过 yield *foo 的方式,你可以在一个 generator function 中执行另外一个 generator function。
1 | function *bar() { |
上面这个例子,就是在 foo 中执行了 bar。
以上就是一些 yield 的一些基本使用方法,那么问题来了,用 yield 究竟可以用来做什么了?先看一个例子:
1 | var https = require('https'); |
通过上面的例子是不是发现了,yield 可以使用在异步调用场景,然后不需要再使用 callback 了,避免 callback hell。关于 yield 和异步调用这块,在后面文章中再接着讨论。