1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-24 10:22:23 +00:00

docs(generator): 使用 for...of 循环依次执行预定的操作步骤

This commit is contained in:
ruanyf 2016-09-05 18:53:57 +08:00
parent 8c06336a27
commit 3cbb8476e3
2 changed files with 56 additions and 77 deletions

View File

@ -348,46 +348,6 @@ for (let n of fibonacci()) {
从上面代码可见,使用`for...of`语句时不需要使用`next`方法。 从上面代码可见,使用`for...of`语句时不需要使用`next`方法。
由于`for...of`循环会自动依次执行`yield`命令这启发我们可以将一些按步骤操作的任务写在Generator函数里面。
```javascript
let steps = [step1Func, step2Func, step3Func];
function *iterateSteps(steps){
for (var i=0; i< steps.length; i++){
var step = steps[i];
yield step();
}
}
```
上面代码中,数组`steps`封装了一个任务的多个步骤Generator函数`iterateSteps`则是依次为这些步骤加上`yield`命令。
将任务分解成步骤之后,还可以将项目分解成多个依次执行的任务。
```javascript
let jobs = [job1, job2, job3];
function *iterateJobs(jobs){
for (var i=0; i< jobs.length; i++){
var job = jobs[i];
yield *iterateSteps(job.steps);
}
}
```
上面代码中,数组`jobs`封装了一个项目的多个任务Generator函数`iterateJobs`则是依次为这些任务加上`yield *`命令(`yield *`命令的介绍详见后文)。
最后,就可以用`for...of`循环一次性依次执行所有任务的所有步骤。
```javascript
for (var step of iterateJobs(jobs)){
console.log(step.id);
}
```
注意,上面的做法只能用于所有步骤都是同步操作的情况,不能有异步操作的步骤。如果想要依次执行异步的步骤,必须使用下一章介绍的方法。
利用`for...of`循环可以写出遍历任意对象object的方法。原生的JavaScript对象没有遍历接口无法使用`for...of`循环通过Generator函数为它加上这个接口就可以用了。 利用`for...of`循环可以写出遍历任意对象object的方法。原生的JavaScript对象没有遍历接口无法使用`for...of`循环通过Generator函数为它加上这个接口就可以用了。
```javascript ```javascript
@ -1298,7 +1258,7 @@ step1(function (value1) {
采用Promise改写上面的代码。 采用Promise改写上面的代码。
```javascript ```javascript
Q.fcall(step1) Promise.resolve(step1)
.then(step2) .then(step2)
.then(step3) .then(step3)
.then(step4) .then(step4)
@ -1313,12 +1273,12 @@ Q.fcall(step1)
上面代码已经把回调函数改成了直线执行的形式但是加入了大量Promise的语法。Generator函数可以进一步改善代码运行流程。 上面代码已经把回调函数改成了直线执行的形式但是加入了大量Promise的语法。Generator函数可以进一步改善代码运行流程。
```javascript ```javascript
function* longRunningTask() { function* longRunningTask(value1) {
try { try {
var value1 = yield step1(); var value2 = yield step1(value1);
var value2 = yield step2(value1); var value3 = yield step2(value2);
var value3 = yield step3(value2); var value4 = yield step3(value3);
var value4 = yield step4(value3); var value5 = yield step4(value4);
// Do something with value4 // Do something with value4
} catch (e) { } catch (e) {
// Handle any error from step1 through step4 // Handle any error from step1 through step4
@ -1329,53 +1289,72 @@ function* longRunningTask() {
然后,使用一个函数,按次序自动执行所有步骤。 然后,使用一个函数,按次序自动执行所有步骤。
```javascript ```javascript
scheduler(longRunningTask()); scheduler(longRunningTask(initialValue));
function scheduler(task) { function scheduler(task) {
setTimeout(function() { var taskObj = task.next(task.value);
var taskObj = task.next(task.value); // 如果Generator函数未结束就继续调用
// 如果Generator函数未结束就继续调用 if (!taskObj.done) {
if (!taskObj.done) { task.value = taskObj.value
task.value = taskObj.value scheduler(task);
scheduler(task); }
}
}, 0);
} }
``` ```
注意yield语句是同步运行不是异步运行否则就失去了取代回调函数的设计目的了。实际操作中一般让yield语句返回Promise对象。 注意,上面这种做法,只适合同步操作,即所有的`task`都必须是同步的,不能有异步操作。因为这里的代码一得到返回值,就继续往下执行,没有判断异步操作何时完成。如果要控制异步的操作流程,详见下一节。
下面,利用`for...of`循环会自动依次执行`yield`命令的特性,提供一种更一般的控制流管理的方法。
```javascript ```javascript
var Q = require('q'); let steps = [step1Func, step2Func, step3Func];
function delay(milliseconds) { function *iterateSteps(steps){
var deferred = Q.defer(); for (var i=0; i< steps.length; i++){
setTimeout(deferred.resolve, milliseconds); var step = steps[i];
return deferred.promise; yield step();
}
} }
function* f(){
yield delay(100);
};
``` ```
上面代码使用Promise的函数库`Q``yield`语句返回的就是一个Promise对象 上面代码中,数组`steps`封装了一个任务的多个步骤Generator函数`iterateSteps`则是依次为这些步骤加上`yield`命令。
如果`yield`语句后面的参数,是一个具有遍历器接口的对象,`yield`会遍历这个对象,再往下执行。这意味着, 将任务分解成步骤之后,还可以将项目分解成多个依次执行的任务。
多个任务按顺序一个接一个执行时,`yield`语句可以按顺序排列。多个任务需要并列执行时比如只有A任务和B任务都执行完才能执行C任务可以采用数组的写法。
```javascript ```javascript
function* parallelTasks() { let jobs = [job1, job2, job3];
let [resultA, resultB] = yield [
taskA(), function *iterateJobs(jobs){
taskB() for (var i=0; i< jobs.length; i++){
]; var job = jobs[i];
console.log(resultA, resultB); yield *iterateSteps(job.steps);
}
} }
``` ```
上面代码中,`yield`语句的参数是一个数组成员就是两个任务taskA和taskB只有等这两个任务都完成了才会接着执行下面的语句。 上面代码中,数组`jobs`封装了一个项目的多个任务Generator函数`iterateJobs`则是依次为这些任务加上`yield *`命令(`yield *`命令的介绍详见后文)。
最后,就可以用`for...of`循环一次性依次执行所有任务的所有步骤。
```javascript
for (var step of iterateJobs(jobs)){
console.log(step.id);
}
```
再次提醒,上面的做法只能用于所有步骤都是同步操作的情况,不能有异步操作的步骤。如果想要依次执行异步的步骤,必须使用下一章介绍的方法。
`for...of`的本质是一个`while`循环,所以上面的代码实质上执行的是下面的逻辑。
```javascript
var it = iterateJobs(jobs);
var res = it.next();
while (!res.done){
var result = res.value;
// ...
res = it.next();
}
```
### 3部署iterator接口 ### 3部署iterator接口

View File

@ -151,7 +151,7 @@
- Mahdi Dibaiee, [ES7 Array and Generator comprehensions](http://dibaiee.ir/es7-array-generator-comprehensions/)ES7的Generator推导 - Mahdi Dibaiee, [ES7 Array and Generator comprehensions](http://dibaiee.ir/es7-array-generator-comprehensions/)ES7的Generator推导
- Nicolas Bevacqua, [ES6 Generators in Depth](http://ponyfoo.com/articles/es6-generators-in-depth) - Nicolas Bevacqua, [ES6 Generators in Depth](http://ponyfoo.com/articles/es6-generators-in-depth)
- Axel Rauschmayer, [ES6 generators in depth](http://www.2ality.com/2015/03/es6-generators.html): Generator规格的详尽讲解 - Axel Rauschmayer, [ES6 generators in depth](http://www.2ality.com/2015/03/es6-generators.html): Generator规格的详尽讲解
- Derick Bailey, [Using ES6 Generators To Short-Circuit Hierarchical Data Iteration](https://derickbailey.com/2015/10/05/using-es6-generators-to-short-circuit-hierarchical-data-iteration/) - Derick Bailey, [Using ES6 Generators To Short-Circuit Hierarchical Data Iteration](https://derickbailey.com/2015/10/05/using-es6-generators-to-short-circuit-hierarchical-data-iteration/):使用 for...of 循环完成预定的操作步骤
## Promise对象 ## Promise对象