标签:
跟上一篇“一次Promise 实践:异步任务的分组调度” 一样,这次的同样是来自于工作中实际碰到的问题。
这次遇到的问题可以比喻成要对一批原料进行加工,其中开头的一些工序是一样的,好比流水线,开头一段是一样的,后面却分为两道流水,在此基础上分别生产不同的产品。
o--o--o 产品1 / 原料o--o--o--o o--o--o 产品2
这里的原料就是要处理的原始数据,可以用数组表示:
var raw = [1, 2, 3, 4, 5]
各个原始数据经过公共部分处理以后应该得到:
[comm(1), comm(2), comm(3), comm(4), comm(5)]
经过两条流水线分别处理后应该得到:
p1 = [p1(comm(1)), p1(comm(2)), p1(comm(3)), p1(comm(4)), p1(comm(5))]
p2 = [p2(comm(1)), p2(comm(2)), p2(comm(3)), p2(comm(4)), p2(comm(5))]
方案1: 有了上一次的经验,我想通过动态的创建一系列then 链条来完成工作,分别用两个promise 对象来保存两条流水线的最终结果:
var main = Promise.resolve() var branch1 = Promise.resolve([]) var branch2 = Promise.resolve([]) var list = [1, 2, 3, 4, 5] list.forEach(function (id) { main = main.then(function () {return "comm(" + id + ")" }) .then(function (comm) { branch1 = branch1.then(path1(comm)) branch2 = branch2.then(path2(comm)) }) }) setTimeout(function () { branch1.then(function (final) { console.log("branch1: ") console.log(final) }) branch2.then(function (final) { console.log("branch2: ") console.log(final) }) }, 0) function path2(id) { return function (prev) { var ret = "bran2(" + id + ")" prev.push(ret) return prev } } function path1(id) { return function (prev) { var ret = "bran1(" + id + ")" prev.push(ret) return prev } }
打印结果:
branch1: [ ‘bran1(comm(1))‘, ‘bran1(comm(2))‘, ‘bran1(comm(3))‘, ‘bran1(comm(4))‘, ‘bran1(comm(5))‘ ] branch2: [ ‘bran2(comm(1))‘, ‘bran2(comm(2))‘, ‘bran2(comm(3))‘, ‘bran2(comm(4))‘, ‘bran2(comm(5))‘ ]
这里需要注意的是在收集branch1 和 branch2 的结果时需要使用setTimeout。依我对runtime的有限理解,是因为要确保回调函数不在本次tick 中被加入事件队列,因为forEach中的那些处理过程需要先于收集过程执行。
这样就清晰的把两个流水线上的数据分别保存在两个promise 对象中,看起来非常讨喜。
方案二,不使用promise 对象来保存结果
var total = [] var p1r = [] var p2r = [] list.forEach(function (id) { main = main.then(function() { return "comm(" + id + ")" }) .then(function (comm) { var promises = [path1(comm), path2(comm)] return Promise.all(promises) }) .then(function (re) { total.push(re) }) }) g.then(function() { total.forEach(function (e) { p1r.push(e[0]) p2r.push(e[1]) }) console.log("p1") console.log(p1r) console.log("p2") console.log(p2r) }) function path1(d) { return Promise.resolve("p1(" + d + ")") } function path2(d) { return Promise.resolve("p2(" + d + ")") }
这个方法其实跟方案一大同小异,只是没有再使用两个promise 对象,而是使用了普通数组。然后不同分支的操作使用promise.all 来执行,最后的结果做一点点处理。好处是不再有setTimeout了。
方案三,利用递归
function c (list) { var retA = [] var retB = [] return (function() { if (!list || list.length == 0) return [retA, retB] var func = arguments.callee , head = list.shift() return Promise.resolve("common("+ head + ")") .then(function (comm) { return Promise.all([resolveA(comm), resolveB(comm)]) }) .then(function (ret) { retA.push(ret[0]) retB.push(ret[1]) return func(list) }) })() } function resolveA(d) { return Promise.resolve("p1("+ d + ")") } function resolveB(d) { return Promise.resolve("p2("+ d + ")") }
调用:
c([1, 2, 3, 4, 5]).then(function (e) { console.log(e) })
结果:
[ [ ‘p1(common(1))‘, ‘p1(common(2))‘, ‘p1(common(3))‘, ‘p1(common(4))‘, ‘p1(common(5))‘ ], [ ‘p2(common(1))‘, ‘p2(common(2))‘, ‘p2(common(3))‘, ‘p2(common(4))‘, ‘p2(common(5))‘ ] ]
第三个方案其实是方案二的递归版本,但是比方案二更简洁,递归的引入使代码变得很短。
思考:如何添加更多的分支?是否可以将整个过程模块化,框架化?哪个方案更容易拓展?(我个人倾向于使用方案一,因为最终得到两个promise 对象后我可以继续then下去……)
标签:
原文地址:http://www.cnblogs.com/agentgamer/p/4655955.html