2015-09-02 44 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());