2017-04-18 136 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 + ')'); 
 
}