V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
redyyu
V2EX  ›  JavaScript

ES6 原生的 Promise 只有 then 和 catch 没有 finally,谁有靠谱的方式加上?

  •  
  •   redyyu · 2015-07-14 19:49:18 +08:00 · 20361 次点击
    这是一个创建于 3427 天前的主题,其中的信息可能已经有所发展或是发生改变。

    ES6 原生的 Promise 只有then 和 catch 没有 finally,谁有靠谱的方式加上?

    已知的不太靠谱方法,有自己增加一个属性的方式finally,在里面调用 then 或者 catch 但这样只是一个then和catch的合体,并不是真的finally,它不会排在所有 then 所有 catch之后执行。

    例如
    promise = new Promise(...)
    promise.finally().then()
    这里后面的 then 会在 finally之后执行,
    而真正的finally 应该在所有的 then 或者 catch 完结以后执行。
    手工把finally写最后只是掩耳盗铃,
    不能假设从此后这个promise不再增加其他的 then 或者catch

    比如
    promise.then().finally()
    ....

    if xxx:
    promise.then(...)

    9 条回复    2015-07-27 19:01:57 +08:00
    otakustay
        1
    otakustay  
       2015-07-15 10:34:48 +08:00
    finally比较好加,按照现在社区的讨论,finally的特点如下:

    1. 不接收任何参数,原来的value或者Error在finally里是收不到的
    2. 处理后不影响原Promise的状态,该reject还是reject,该resolve还是resolve
    3. 不影响Promise向后传递的传,resolve状态还是传递原来的value,reject状态还是传递原来的Error

    所以代码这样就行了

    Promise.prototype.finally = function (callback) {
    var Promise = this.constructor;
    return this.then(
    function (value) {
    Promise.resolve(callback()).then(
    function () {
    return value;
    }
    );
    },
    function (reason) {
    Promise.resolve(callback()).then(
    function () {
    throw reason;
    }
    );
    }
    );
    }
    otakustay
        2
    otakustay  
       2015-07-15 10:34:56 +08:00
    代码自己拿去格式化吧,gist太麻烦
    otakustay
        3
    otakustay  
       2015-07-15 10:37:55 +08:00
    另外你的“finally要在所有then和catch完成后执行”的说法是错的,仔细想想是不是“catch要在then后执行”呢?毕竟是先try后catch嘛

    Promise的then和catch是一种“衍生”,即会“生”出一个新的Promise来,相当于串行模式下从一个函数跳到了另一个函数,而之前的finally是“前一个函数中的finally”,所以并不是如你说的finally要在最后
    redyyu
        4
    redyyu  
    OP
       2015-07-15 21:31:52 +08:00
    你给的这一段我在提问前自己也写过了。它虽然可以捕获 chain 之前的 then 或者 catch 然而并没有什么卵用,看下面这个链

    ```coffee
    ajax(url)
    .then ->
    go_uniqlo()
    .then ->
    meet_girl()
    .catch ->
    throw new Error('Impotence')
    .finally ->
    fuck in the dressing room ...

    ```

    那么这里的都需要代码写下之时手动写好,
    如果在后面动态加入

    .then ->
    pick_up_cell_phone()
    .then ->
    open_video_app()

    会排在finally 后面 丧失了 finally 的意义,这样的情况之前用 finally 只要用then 替代 就可以了
    而且相信 在现实使用中,极少极少会有 情况说是 在 链式调用 半路当中 需要 同时处理 成功和错误两种情况之后,还要继续 then 或者 catch 下去的。

    而我之前说的这种 链式调用 已经安排好以后 又由其他条件加入的情况 可以很常见。
    我在之前一个 angular material 的项目中平凡使用这种方式 在页面上显示 Toast (上传文件,全部传完,所有链式调用跑完,显示toast,中途添加文件,链数增加,finally 依旧在最后 显示 toast)

    因此,你理解的finally 和我说的 finally 不同。

    在这里我希望可以实现 angularjs 中 的 q deff 的 finally 效果。

    这个也其他的promise 实现中也有叫 complete 或者 done 的。

    它需要能够排在链式调用的最后一位,无条件执行。
    也相当于 switch 函数的 default

    对于这个问题,我现在基本上确定要么用其他的库,ES6 本身的 Promise 并没有这个,也许以后也不会有,在他们的 issues 中提过你提供的这一段代码,我觉得这个只是一个凑合的办法,而且它仅仅是方便了 链式调用,写死最后的那一下。
    xieranmaya
        5
    xieranmaya  
       2015-07-17 10:31:16 +08:00   ❤️ 1
    这么实现会不会看着简单一些?

    Promise.prototype.finally = function(fn) {
    function finFn(valueORreason){
    fn.call(null)
    }
    this.then(finFn, finFn)
    return this
    }
    xieranmaya
        6
    xieranmaya  
       2015-07-17 11:10:44 +08:00
    调整了一下,这样可以保证finally一定最后执行,但是finally返回的是this而不是一个新的Promise
    https://gist.github.com/xieranmaya/5f817d85823dcb108fc9
    xieranmaya
        7
    xieranmaya  
       2015-07-17 11:28:28 +08:00
    如果想要返回一个新的Promise,this.then一下就好了
    rekey
        8
    rekey  
       2015-07-27 11:12:31 +08:00
    我只是来看妹纸的。
    redyyu
        9
    redyyu  
    OP
       2015-07-27 19:01:57 +08:00
    @xieranmaya 好像行。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   948 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 23:00 · PVG 07:00 · LAX 15:00 · JFK 18:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.