step() 除了接收 function 类型参数,还可以接收 Array 类型。Array参数会和后一个 function 绑定,作为 function 执行时的参数列表。
这个机制主要应用于循环当中:
var Steps =require("ocsteps");
Steps(
// 有问题的方式
function(){
console.log("Always prints the last time value of the variable i in the for loop: ");
for(var i=1;i<=3;i++)
{
this.step(function(){
console.log(i);
})
}
}
// 常用的方式
,function(){
console.log("Another way: ");
for(var i=1;i<=3;i++)
{
(function(arg){
this.step(function(){
console.log(arg);
});
})(i);
}
}
// 更好的方式
,function(){
console.log("Better way: the value of variable i in each loop has saved, and then pass to step functions: ");
for(var i=1;i<=3;i++)
{
this.step([i],function(arg){
console.log(arg);
});
}
}
)();
输出的结果是:
Always prints the last time value of the variable i in the for loop:
3
3
3
correct way:
1
2
3
Better way: the value of variable i in each loop has saved, and then pass to step functions:
1
2
3
如果在循环中使用 this.step() ,并在 step function 里访问闭包变量,可能完全不是你想要的结果。
例子里的三种方式,第一种方式是有问题的:执行 step function 输出闭包变量i时,循环已经结束了,所以i总是等于最后一次循环过程中的值:3;
第二种方式可以避免这个问题,这是闭包编程中避免此类问题的常见模式;而第三种方式更简单。
绑定对象
可以用 bind() 将任务链绑定到一个对象上(这样就可以少用一个闭包变量了),然后在 step function 内,this 能够继承该对象的所有属性和方法。
var Steps =require("ocsteps");
var object ={
foo:'bar'
,toString:function(){
returnthis.foo;
}
}
Steps(
function(){
console.log(this.foo);
console.log(this);
}
).bind(object)();
调用 bind() 后,this.object 指向被绑定的对象
由于 ie 不支持 proto,bind() 在全系列 ie 下无效(但不会报错)。
bind()是一个为框架作者提供的方法,他们可以将 ocSteps 整合到他们的框架中。例如将任务链绑定给控制器,就得到了一个支持异步操作的控制器,那些 step function 实际上就成了控制器的方法。
循环
loop
var Steps =require("ocsteps");
Steps(
function()
{
this.loop(function(i){
if(i>3)
{
// 结束循环
this.break(i);
}
console.log('loop ',i);
return++i ;
});
// 传个 loop step 的参数
return0;
}
// break() 会将收到的参数传递给循环结束后的下一个step
,function(i)
{
console.log('break ',i);
}
)();
输出:
loop 0
loop 1
loop 2
loop 3
break 4
在循环中同样可以调用 hold() 和 step() 等方法
var Steps =require("ocsteps");
Steps(
function()
{
this.loop(function(i){
if(i>3)
{
// 结束循环
this.break(i);
}
console.log('loop ',i);
// 异步操作
var release =this.hold();
setTimeout(function(){
// 1秒钟后,将 i 传递给 step
release(i);
},1000);
this.step(function(i){
console.log('step in loop ',i);
// 传递给下一次 loop
return++i ;
});
});
// 传个 loop step 的参数
return0;
}
// break() 会将收到的参数传递给循环结束后的下一个step
,function(i)
{
console.log('break ',i);
}
)();
loop 0
step in loop 0
loop 1
step in loop 1
loop 2
step in loop 2
loop 3
step in loop 3
break 4
loop() 实际上和 while(1) 等效 。
each
each() 是更有用的循环方式,它支持对象成员和数组元素的遍历。
var fs =require("fs");
functionprintDir(folder,callback){
Steps(
function()
{
fs.readdir(folder,this.hold());
}
,function(err,files)
{
if(err)thrownewError(err);
// 遍历所有目录成员
this.each(files,function(i,filename){
var path = folder+'/'+filename ;
fs.stat(path,function(err,stat)
{
if(err)thrownewError(err);
if(stat.isDirectory())
{
console.log('folder:',path);
// 递归子目录
printDir(path,this.hold());
}
else
{
console.log('file:',path);
}
});
});
}
).done(function(err){
callback &&callback(err);
})();
}
// 深度优先遍历目录
printDir("/some/folder");
分支
var Steps =require("ocsteps");
Steps(
function(){
console.log("master step 1");
return'a';
}
,function(arg){
console.log("master step 2, ","input:",arg);
// 创建一个分支
this.fork(
function(arg){
console.log("branch step 1, ","input: ",arg);
return'c';
}
,function(arg){
console.log("branch step 2, ","input: ",arg);
return'd';
}
);
return'b'
}
,function(arg){
console.log("master step 3, ","input: ",arg);
}
)();
输出:
master step 1
master step 2, input: a
branch step 1, input: b
branch step 2, input: c
master step 3, input: d
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.