From 3cbb8476e360a8366c9035724fc7823d509bf211 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Mon, 5 Sep 2016 18:53:57 +0800 Subject: [PATCH] =?UTF-8?q?docs(generator):=20=E4=BD=BF=E7=94=A8=20for...o?= =?UTF-8?q?f=20=E5=BE=AA=E7=8E=AF=E4=BE=9D=E6=AC=A1=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E9=A2=84=E5=AE=9A=E7=9A=84=E6=93=8D=E4=BD=9C=E6=AD=A5=E9=AA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/generator.md | 131 +++++++++++++++++++--------------------------- docs/reference.md | 2 +- 2 files changed, 56 insertions(+), 77 deletions(-) diff --git a/docs/generator.md b/docs/generator.md index 4e75f41..315d164 100644 --- a/docs/generator.md +++ b/docs/generator.md @@ -348,46 +348,6 @@ for (let n of fibonacci()) { 从上面代码可见,使用`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函数为它加上这个接口,就可以用了。 ```javascript @@ -1298,7 +1258,7 @@ step1(function (value1) { 采用Promise改写上面的代码。 ```javascript -Q.fcall(step1) +Promise.resolve(step1) .then(step2) .then(step3) .then(step4) @@ -1313,12 +1273,12 @@ Q.fcall(step1) 上面代码已经把回调函数,改成了直线执行的形式,但是加入了大量Promise的语法。Generator函数可以进一步改善代码运行流程。 ```javascript -function* longRunningTask() { +function* longRunningTask(value1) { try { - var value1 = yield step1(); - var value2 = yield step2(value1); - var value3 = yield step3(value2); - var value4 = yield step4(value3); + var value2 = yield step1(value1); + var value3 = yield step2(value2); + var value4 = yield step3(value3); + var value5 = yield step4(value4); // Do something with value4 } catch (e) { // Handle any error from step1 through step4 @@ -1329,53 +1289,72 @@ function* longRunningTask() { 然后,使用一个函数,按次序自动执行所有步骤。 ```javascript -scheduler(longRunningTask()); +scheduler(longRunningTask(initialValue)); function scheduler(task) { - setTimeout(function() { - var taskObj = task.next(task.value); - // 如果Generator函数未结束,就继续调用 - if (!taskObj.done) { - task.value = taskObj.value - scheduler(task); - } - }, 0); + var taskObj = task.next(task.value); + // 如果Generator函数未结束,就继续调用 + if (!taskObj.done) { + task.value = taskObj.value + scheduler(task); + } } ``` -注意,yield语句是同步运行,不是异步运行(否则就失去了取代回调函数的设计目的了)。实际操作中,一般让yield语句返回Promise对象。 +注意,上面这种做法,只适合同步操作,即所有的`task`都必须是同步的,不能有异步操作。因为这里的代码一得到返回值,就继续往下执行,没有判断异步操作何时完成。如果要控制异步的操作流程,详见下一节。 + +下面,利用`for...of`循环会自动依次执行`yield`命令的特性,提供一种更一般的控制流管理的方法。 ```javascript -var Q = require('q'); +let steps = [step1Func, step2Func, step3Func]; -function delay(milliseconds) { - var deferred = Q.defer(); - setTimeout(deferred.resolve, milliseconds); - return deferred.promise; +function *iterateSteps(steps){ + for (var i=0; i< steps.length; i++){ + var step = steps[i]; + yield step(); + } } - -function* f(){ - yield delay(100); -}; ``` -上面代码使用Promise的函数库`Q`,`yield`语句返回的就是一个Promise对象。 +上面代码中,数组`steps`封装了一个任务的多个步骤,Generator函数`iterateSteps`则是依次为这些步骤加上`yield`命令。 -如果`yield`语句后面的参数,是一个具有遍历器接口的对象,`yield`会遍历这个对象,再往下执行。这意味着, - -多个任务按顺序一个接一个执行时,`yield`语句可以按顺序排列。多个任务需要并列执行时(比如只有A任务和B任务都执行完,才能执行C任务),可以采用数组的写法。 +将任务分解成步骤之后,还可以将项目分解成多个依次执行的任务。 ```javascript -function* parallelTasks() { - let [resultA, resultB] = yield [ - taskA(), - taskB() - ]; - console.log(resultA, resultB); +let jobs = [job1, job2, job3]; + +function *iterateJobs(jobs){ + for (var i=0; i< jobs.length; i++){ + var job = jobs[i]; + 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接口 diff --git a/docs/reference.md b/docs/reference.md index 739693f..9622b16 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -151,7 +151,7 @@ - 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) - 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对象