起因是在工作中遇到一个问题,可以用一个二维数组简单描述:
[[1,2,3],[4,5,6],[7,8,9]]
这里每个数字都代表“一个异步计算任务”, 每个子数组把1个或多个计算任务划分成组,要求是:每组内的计算任务并行执行,但是各个组间要顺序执行。具体说来就是先执行1,2,3 等获得全部的结果以后再执行4,5,6以此类推。最后返回的结果跟执行顺序相同。
经过了大概半小时的尝试与思考,我写出了一个版本:
function dispatch(groups) { var result = Promise.resolve([]) groups.forEach(function (group) { result = result.then(function (prevVal) { var ps = [] group.forEach(function (task) { ps.push(Promise.resolve(task)) }) return Promise.all(ps).then(function (r) { prevVal.push(r) return prevVal }) }) }) return result }
调用方法:
dispatch([[1,2,3],[4,5,6],[7,8,9]]).then(function (result) { console.log(result) //[[1,2,3],[4,5,6],[7,8,9]] });
基本思路就是,从第一组开始,对其中的所有异步任务求值,求值成功后再开始第二组...最后当遍历完全部组后,返回结果。
可以简单描述为:resolve([1,2,3]).then([4,5,6]).then([7,8,9])
思路看似简单直观,但是如何动态的创建then 链条确实花费了我不少脑筋,写完代码后也对promise 有了更深刻的理解。
写完代码后我兴高采烈地拿给同事看,同事说我这个写得太复杂不容易看懂,他能写出一个更简单直观的递归方案,大概十分钟后他拿出代码:
function dispatch(groups) { var results = [] return (function () { var fun = arguments.callee , group = groups.shift() if (!group) { return Promise.resolve(results) } var promises = [] group.forEach(function (task) { promises.push( Promise.resolve(task) ) }) return Promise.all(promises).then(function (rets) { results.push(rets) return fun() }) }()) }
貌似真的比我那个要直观,但是貌似这个方案要比我那个多创建一个Promise 对象,而且代码行数也略多一点。无论怎样我最重还是选了同事的方案。。毕竟可读性第一嘛。