这篇文章的起源是这样一段代码
这段代码的作用很简单,就是让一系列的动画,按顺序执行。而这样一层一层嵌套进去的作用就是一个执行完了,执行下一个。
为了解决这个问题我就想到了是不是可以把这些事件放到一个数组里,然后依次执行呢?也就是说实现一个seq(tasks,finally)
函数
/**
* @param {function []} tasks - function array
* @param {function} cb - 执行完的回调
*/
function seq(tasks, cb) {
}
我的思路是每次执行完一个function
之后再执行下一个函数,如果执行到最后一个函数就执行最后的cb
即
function seq(tasks, cb) {
(function runTask(i) {
var task = tasks[i];
task(function() {
i++;
if (i !== tasks.length) {
runTask(i);
} else {
cb();
}
});
})(0);
}
但是显然这么实现并不优雅(Zhuāng Bī),那么有没有更优雅的方案呢?显然是有的。我们可以使用ES5
的reduceRight
函数(MSDN文档)
如果你并不知道这个函数的话,我们就要从和它差不多的reduce
函数(MSDN文档)说起了。reduce
函数接收两个参数callback
、initialValue
-
callback
执行数组中每个值的函数,包含四个参数previousValue
上一次调用回调返回的值,或者是提供的初始值(initialValue)currentValue
数组中当前被处理的元素index
当前元素在数组中的索引array
调用 reduce 的数组
initialValue
作为第一次调用 callback 的第一个参数。
/**
@examples
**/
var array = [
{
id: 1,
name: "小李"
}, {
id: 2,
name: "小张"
}, {
id: 3,
name: "小刘"
}, {
id: 4,
name: "小王"
}
]
var objMap = array.reduce(function(map,nowObj,index,array) {
map[nowObj.id] = nowObj;
return map;
},{});
很简单的就把一个array,转成了一个以id
为key
,obj
为value
的ObjMap
有木有?就是这么简单。reduceRight
函数的参数和reduce
函数一样,只不过是执行的顺序与reduce
函数相反,是倒过来执行的。这样的话我们就可以把上面那个tasks转换成类似最一开始那张图一样的结构。
function seq2(tasks, cb) {
tasks.reduceRight(function(cb, task) {
return function() {
return task(cb);
};
}, cb)();
}
//这段代码的推导如下
//第一次执行
function() {
return task5(cb);
};
//第二次执行
function() {
return task4(function() {
return task5(cb);
});
}
//第三次执行
function() {
return task3(function() {
return task4(function() {
return task5(cb);
});
});
};
//第四次执行
function() {
return task2(function() {
return task3(function() {
return task4(function() {
return task5(cb);
});
});
})
};
//最后一次执行
function() {
return task1(function() {
return task2(function() {
return task3(function() {
return task4(function() {
return task5(cb);
});
});
});
});
};
这样就得到了一个跟上面最一开始那张图一模一样的一个function,然后最重要的是reduceRight
面那个()
,他就是启动这个最终返回function的开关。
测试用例如下:
var tasks = [1, 2, 3, 4, 5];
tasks = tasks.map(function(i) {
return function(cb) {
setTimeout(function() {
console.log(i);
cb();
}, Math.random() * 500 | 0);
}
});
seq(tasks, function() {
console.log('all Done');
});
seq2(tasks, function() {
console.log('all Done');
});
这个问题是我公司的一位同事(老师)给我出的题目。第二个方案,也是他给我的思路。感谢我的这位老师,虽然在工作中我说的不多,但是在我内心里,我对您的崇拜那是简直了。哈哈。最后是github源代码
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。