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:
parent
8c06336a27
commit
3cbb8476e3
@ -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接口
|
||||||
|
|
||||||
|
@ -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对象
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user