ForeveHG / Frontend-Daily-Interview

学习,尝试回答一些前端面试题
1 stars 0 forks source link

15. 模拟实现一个 Promise.finally #15

Open ForeveHG opened 5 years ago

ForeveHG commented 4 years ago

finally方法返回一个Promise,在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数,上一个最直接的版本:

Promise.prototype.myFinally = function (callback) {
    return this.then(callback,callback)
}

这种写法callback将接收到前一步promise返回的值,那么finally返回的promise中的value就是callback这个函数的返回值,但ES6的Promise.finally不是这样的,看下面的例子

// Promise {<resolved>: 2}
new Promise(resolve => {
    resolve(1)
}).myFinally(res => {
    return 2
})

// 但ES6的finally返回的却是Promise {<resolved>: 1}
new Promise(resolve => {
    resolve(1)
}).finally(res => {
    return 2
})

也就是说finally返回的promise的value应该是前一个promise的返回值,callback这个函数本身得返回值不作为finally返回的promise的value,callback只是无论成功失败都要执行的一个函数而已,所以修改一下代码

Promise.prototype.myFinally = function (callback) {
    return this.then(value => {
    callback();
    return value;
    },reason => {
        callback();
    throw reason;
    })
}

测试一下

//自己定义的finally: Promise {<resolved>: 1}
new Promise(resolve => {
    resolve(1)
}).myFinally(res => {
    return 2
})

// ES6的finally: Promise {<resolved>: 1}
new Promise(resolve => {
    resolve(1)
}).finally(res => {
    return 2
})

但当callback是一个异步操作,并返回一个promise对象时,这种写法并不会等待这个异步操作执行结束后再进行下一步,但ES6中promise的finally是会等待callback返回的promise对象resolve后再进行下一步的,所以需要把代码更改为

Promise.prototype.myFinally = function (callback) {
    return this.then(
        value => Promise.resolve(callback()).then(() => value),
        reason => Promise.resolve(callback()).then(() => Promise.reject(reason))
    )
}

可以查看这个polyfill

/**
 * @this {Promise}
 */
function finallyConstructor(callback) {
  var constructor = this.constructor;
  return this.then(
    function(value) {
      // @ts-ignore
      return constructor.resolve(callback()).then(function() {
        return value;
      });
    },
    function(reason) {
      // @ts-ignore
      return constructor.resolve(callback()).then(function() {
        // @ts-ignore
        return constructor.reject(reason);
      });
    }
  );
}

其中用到了constructor,我感觉主要是为了兼容性,毕竟也有很多自己实现的Promise库

carpenterzoe commented 3 years ago

谢谢,网上找了好多相关资料,都是不清不楚的,终于在你这看明白了。关注你啦哈哈哈