2015-09-02 181 views
38

我想運行相同的操作,無論我的Promise是否成功解決。我不想將相同的函數綁定到.then的兩個參數。是不是像jQuery一樣有.always?如果不是,我該如何實現?ES6承諾結算回調?

+1

不能這樣做。最後(函數(){...})? –

+0

查看http://stackoverflow.com/questions/26667598/will-javascript-es6-promise-support-done-api –

+0

@CharlieWynn它在Babel中未定義,未列在[MDN](https://developer.mozilla。組織/ EN-US /文檔/網絡/的JavaScript /參考/ Global_Objects /無極/捕獲)。 – mpen

回答

28

是不是有像jQuery一樣的.always

不,there's not (yet)。雖然有一個active proposal,所以也許E​​S2018。

如果不是,我該如何做到這一點?更廣泛地

Promise.prototype.finally = function(cb) { 
    const res =() => this 
    const fin =() => Promise.resolve(cb()).then(res) 
    return this.then(fin, fin); 
}; 

,或者與傳遞分辨率信息回調:

您可以實現finally方法自己這樣

Promise.prototype.finally = function(cb) { 
    const res =() => this 
    return this.then(value => 
     Promise.resolve(cb({state:"fulfilled", value})).then(res) 
    , reason => 
     Promise.resolve(cb({state:"rejected", reason})).then(res) 
    ); 
}; 

兩個確保原始分辨率是持續(當回調中沒有異常時)並且正在等待承諾。

+2

我認爲pollyfill中'this.then'的錯誤回調應該拋出然後返回。 – dfsq

+0

@dfsq:它通過返回原始的,被拒絕的承諾:-) – Bergi

+0

你說得對,因爲'=>'這個'原來的承諾,當然。 – dfsq

6

隨着異步/ AWAIT,你可以的awaittry/finally組合,像這樣:

async function(somePromise) { 
    try { 
    await somePromise(); 
    } finally { 
    // always run this-- even if `somePromise` threw something 
    } 
} 

以下是我已經在生產中使用的節點上運行,用巴貝爾的async-to-generator插件一個真實的例子。

// Wrap promisified function in a transaction block 
export function transaction(func) { 
    return db.sequelize.transaction().then(async t => { 
    Sequelize.cls.set('transaction', t); 
    try { 
     await func(); 

    } finally { 
     await t.rollback(); 
    } 
    }); 
} 

我使用此代碼摩卡測試旁邊的Sequelize ORM內啓動一個數據庫事務,無論DB的結果調用的測試中,始終回滾底。

這大致類似於藍鳥的.finally()方法,但IMO,更好的語法!

注意:。如果你想知道爲什麼我不await荷蘭國際集團第一個承諾 - 這是Sequelize的實現細節它採用CLS爲「捆綁」 SQL事務的無極鏈任何在之內產生,同一個鏈被限制在事務中,外面的任何東西都不是,所以在Promise上等待會關閉事務塊並破壞鏈,我把這個例子展示給你看,'vanilla '承諾處理可以與異步函數一起混合使用,並且可以很好地協同工作。)

+1

爲什麼不使用'async function transaction'並放棄'then'調用呢? – Bergi

+1

整潔!從來沒有想過使用'try/finally'與'async/await'。這會派上用場。 – mpen

+0

@Bergi - Sequelize使用所謂的'CLS'來將事務塊的範圍限定爲Promise鏈。如果我使用'await',它會返回一個事務處理程序,但後續的SQL將被放在該塊的外側,因此不會限制到該事務。這是Sequelize的實現細節。 –

3

如果您沒有/無法更新原型,最終破解方法是:

executeMyPromise() 
.then(function(res){ return {res: res}; }) 
.catch(function(err){ return {err: err}; }) 
.then(function(data) { 
    // do finally stuff 
    if (data.err) { 
     throw data.err; 
    } 
    return data.res; 
}).catch(function(err) { 
    // handle error 
}); 
+2

我不認爲這是一樣的。請參閱[第3和第4點](https://github.com/tc39/proposal-promise-finally#why-not-thenf-f)。我們可以在不修改原型的情況下使用Bergi的解決方案。你只需要調用它:'finallyFunc.call(thePromise,callback)'。與綁定操作符一起工作良好。 – mpen

+0

@mpen,你是對的。修正後編輯。 – user2426679

1

這裏是我的.finally()的實現。

Promise.prototype.finally = function(cb) { 
    return this.then(v=>Promise.resolve(cb(v)), 
        v=>Promise.reject(cb(v))); 
}; 

我測試了它:

(new Promise((resolve,reject)=>{resolve(5);})).finally(x=>console.log(x)); //5 

(new Promise((resolve,reject)=>{reject(6);})).finally(x=>console.log(x)); //6 

(new Promise((resolve,reject)=>{reject(7);})) 
.then(x=>x,y=>y) 
.catch(x=>{throw "error";}) 
.finally(x=>{console.log(x); throw "error"; return x;}) // 7 
.then(x=>console.log(x),y=>console.log('e')); //e 
// Uncaught (in promise) undefined 
0

沒有必要引入新的概念

const promise = new Promise((resolve, reject) => { 
    /*some code here*/ 
}); 

promise.then(() => { 
    /* execute success code */ 
},() => { 
    /* execute failure code here */ 
}).then(() => {},() => {}).then(() => { 
    /* finally code here */ 
}); 
+0

這會導致第一個.then()分支的返回值被丟棄,不是嗎? – thenickdude

+0

這是正確的。 –

0

延長Bergi答案。

在catch處理程序中返回Promise.reject()將防止finnalizing'then'被調用。

所以,如果你要處理的諾言錯誤2+時候你應該使用的樣板是這樣的:

return myPromise() 
.then(() => ...) 
.catch((error) => { 
    ... 
    myFinnaly(); 
    return Promise.reject(error); 
}) 
.then(() => myFinnaly());