2016-12-28 24 views
4

想象一下以下基於承諾,例如:Promise有沒有'最後'的模式?

function treadLightly() { 
    return Promise.resolve() 
    .then(function() { allocateResource(); }) 
    .then(function() { doRiskyOperation(); }) 
    .then(function() { releaseResource(); }) 
    .catch(function() { releaseResource(); }) 
    ; 
} 

我們想打電話releaseResource()與否doRiskyOperation()做出決議或拒絕。但是在兩個不同的地方呼叫releaseResource()有一股代碼味道。

我們真正想要的是與javascript的finally等效的東西。

有沒有一個更清潔的方式來編碼在承諾?

+0

值得一讀這篇文章關於[資源管理使用承諾](http://bluebirdjs.com/docs/api/resource-management.html)甚至'.finally()'並不總是完美的選擇,它討論了使用處置器的設計模式。 – jfriend00

+0

爲什麼用'Promise.resolve()。然後(函數(){allocateResource();})。然後(...)'而不是'allocateResource()。然後(...) ?如果'allocateResource()'不是異步的,那麼爲什麼它在promise鏈中呢? – jfriend00

+0

@ jfriend00:因爲它是一個很好的模式:)。 'allocateResource()'可能會也可能不會返回一個承諾,除非可以看看它的代碼,否則你不會知道。使用'Promise.resolve()'開始一個鏈,使鏈對'allocateResource()'返回的內容不敏感,並且(更重要的是)使意圖清晰。 –

回答

3

雖然@T.J. Crowder's answer是正確的,你的問題的現實解決方案可以在年底增加另外.then

function treadLightly() { 
    return Promise.resolve() 
    .then(function() { allocateResource(); }) 
    .then(function() { doRiskyOperation(); }) 
    .catch(function() { /* Do nothing, or log the error */ }) 
    .then(function() { releaseResource(); }) 
    ; 
} 
+0

很高興知道在作品中有一個最終的作品(和@ T.J。Crowder爲polyfill獲得+1)。但在此期間,這優雅地回答了OP。謝謝... –

+0

@fearless_fool:在一個孤立的例子中,它工作得很好,但在非平凡的現實世界中相當迅速地退化。所以,是的,絕對有一些用處,但要注意將拒絕轉換爲解決方案(這就是這樣做)會誤導後續處理程序。 –

+1

@ T.J.Crowder:正式注意 - 謝謝。我在研究捕獲錯誤的一般方法,調用清理方法(例如releaseResource()),然後重新拒絕錯誤,以避免誤導後續處理程序。或者我可以實施polyfill! –

2

Promise是否存在「最終」模式?

不通過本機(ES6)實現。然而,Bluebird具有這樣的特徵:

return new Promise((resolve, reject) => { 
    ... 
}).finally(() => { 
    ... 
}); 

.finally | bluebird

5

ES2015(ES6)的承諾沒有finally尚未。它有一個Stage 2 proposal,這意味着它基本上沒有機會在ES2017中,但有一些可能性在ES2018中。我還沒有看到一個非常好的模式(與實際的finally功能相反)。

一些第三方承諾庫有它,包括Bluebird,Qwhen。 jQuery的Deferred的承諾也有它,形式爲always

的建議有polyfill你可以使用:

if (typeof Promise !== 'function') { 
    throw new TypeError('A global Promise is required'); 
} 

if (typeof Promise.prototype.finally !== 'function') { 
    var speciesConstructor = function (O, defaultConstructor) { 
     var C = typeof O.constructor === 'undefined' ? defaultConstructor : O.constructor; 
     var S = C[Symbol.species]; 
     return S == null ? defaultConstructor : S; 

     var C = O.constructor; 
     if (typeof C === 'undefined') { 
      return defaultConstructor; 
     } 
     if (!C || (typeof C !== 'object' && typeof C !== 'function')) { 
      throw new TypeError('O.constructor is not an Object'); 
     } 
     var S = C[Symbol.species]; 
     if (S == null) { 
      return defaultConstructor; 
     } 
     if (typeof S === 'function' && S.prototype) { 
      return S; 
     } 
     throw new TypeError('no constructor found'); 
    }; 
    var shim = { 
     finally(onFinally) { 
      var handler = typeof onFinally === 'function' ? onFinally :() => {}; 
      var C; 
      var newPromise = Promise.prototype.then.call(
       this, // throw if IsPromise(this) is not true 
       x => new C(resolve => resolve(handler())).then(() => x), 
       e => new C(resolve => resolve(handler())).then(() => { throw e; }) 
      ); 
      C = speciesConstructor(this, Promise); // throws if SpeciesConstructor throws 
      return newPromise; 
     } 
    }; 
    Promise.prototype.finally = shim.finally; 
}