2017-04-18 66 views
0

新承诺;考虑到情况有promiseA()promiseB(a)取决于第一个结果,我想收集来自两个结果,并执行第三个动作doSomething(a, b)承诺嵌套vs链接风格

样式A(关闭/嵌套)

promiseA().then(function (resultA) { 
    return (promiseB(resultA).then(function (resultB) { 
    doSomething(resultA, resultB); 
    })); 
}); 

样式B(返回值/链)

promiseA().then(function (resultA) { 
    return Promise.all([resultA, promiseB(resultA)]); 
}).spread(function (resultA, resultB) { 
    doSomething(resultA, resultB); 
}); 

据我所知,这些是等效的:

  • 同测序约束promiseApromiseB
  • 之间的最终承诺的回报,如果promiseApromiseB被拒绝undefined
  • 最终承诺被拒绝,或doSomething抛出。

作为样式,样式B减少缩进(末日金字塔)。

但是,样式B更难以重构。如果需要引入中间的promiseA2(a)doSomething(a, a2, b),我需要修改3行(Promise.allspread, doSomething),这可能会导致错误(意外交换等),而样式AI仅用于修改1行(doSomething)和变量名称说明它是哪个结果。在大型项目中,这可能很重要。

这两种风格之间是否存在其他非功能性权衡?更多/更少的内存分配在一个和另一个之间?更多/更少转向事件循环?例外的更好/更糟糕的堆栈跟踪?

+3

'const resultA = await promiseA(); const resultB =等待promiseB(resultA); doSomething(resultA,resultB);' – zerkms

+0

另请参阅[如何访问'.then()'链中的先前承诺结果?](http://stackoverflow.com/q/28250680/1048572)不同的方法 – Bergi

回答

0

我认为这两种方法之间的非功能权衡并不那么重要:第二种方法在创建数组时有一些开销,并且传播相应的结果,并且它会创建一个更多的承诺。然而,在一个异步流程中,我认为所有这些都是微不足道的。

您的主要担心似乎是重构的轻松。

对,我会建议使用的功能的阵列,并且reduce了它:

[promiseA, promiseB, doSomething].reduce((prom, f) => 
 
    prom.then((res = []) => (f(...res) || prom).then([].concat.bind(res))) 
 
, Promise.resolve()); 
 

 

 
// Sample functions 
 
function wait(ms) { 
 
    return new Promise(resolve => setTimeout(resolve, ms)); 
 
} 
 

 
function promiseA() { 
 
    console.log('promiseA()'); 
 
    return wait(500).then(_ => 13); 
 
} 
 

 
function promiseB(a) { 
 
    console.log('promiseB(' + a + ')'); 
 
    return wait(500).then(_ => a + 2); 
 
} 
 

 
function doSomething(a, b) { 
 
    console.log('doSomething(' + a + ',' + b + ')'); 
 
}

的想法是,在then回调链中的下一个函数得到所有的先前的结果作为参数传递。所以如果你想在链中注入一个promise-returns函数,那么这是将它插入到数组中的问题。不过,您需要注意传递的参数:它们在此解决方案中是累积的,因此doSomething不是该规则的例外。

如果在另一方面,你只想doSomething得到所有的结果,只有最近的结果传递给每一个中间函数,则代码应该是这样的:

[promiseA, promiseB].reduce((prom, f) => 
 
    prom.then((res = []) => f(...res.slice(-1)).then([].concat.bind(res))) 
 
, Promise.resolve()).then(res => doSomething(...res)); 
 

 
function wait(ms) { 
 
    return new Promise(resolve => setTimeout(resolve, ms)); 
 
} 
 

 
function promiseA() { 
 
    console.log('promiseA()'); 
 
    return wait(100).then(_ => 13); 
 
} 
 

 
function promiseB(a) { 
 
    console.log('promiseB(' + a + ')'); 
 
    return wait(100).then(_ => a + 2); 
 
} 
 

 
function doSomething(a, b) { 
 
    console.log('doSomething(' + a + ',' + b + ')'); 
 
}