Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

第 64 题:模拟实现一个 Promise.finally #109

Open
lvtraveler opened this issue Apr 28, 2019 · 30 comments
Open

第 64 题:模拟实现一个 Promise.finally #109

lvtraveler opened this issue Apr 28, 2019 · 30 comments
Labels

Comments

@lvtraveler
Copy link

lvtraveler commented Apr 28, 2019

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};
@xueqingxiao
Copy link

https://github.com/matthew-andrews/Promise.prototype.finally/blob/master/finally.js

@wingmeng
Copy link

wingmeng commented Apr 28, 2019

window.Promise && !('finally' in Promise) && !function() {        
  Promise.prototype.finally = function(cb) {
    cb = typeof cb === 'function' ? cb : function() {};
      
    var Fn = this.constructor;  // 获取当前实例构造函数的引用

    // 接受状态:返回数据
    var onFulfilled = function(data) {
      return Fn.resolve(cb()).then(function() {
        return data
      })
    };

    // 拒绝状态:抛出错误
    var onRejected = function(err) {
      return Fn.resolve(cb()).then(function() {
        throw err
      })
    };

    return this.then(onFulfilled, onRejected);
  }
}();

/*********************** 测试 ***********************/
const p = new Promise((resolve, reject) => {
  console.info('starting...');

  setTimeout(() => {
    Math.random() > 0.5 ? resolve('success') : reject('fail');
  }, 1000);
});

// 正常顺序测试
p.then((data) => {
    console.log(`%c resolve: ${data}`, 'color: green')
  })
  .catch((err) => {
    console.log(`%c catch: ${err}`, 'color: red')
  })
  .finally(() => {
    console.info('finally: completed')
  });

// finally 前置测试  
p.finally(() => {
    console.info('finally: completed')
  })	
  .then((data) => {
    console.log(`%c resolve: ${data}`, 'color: green')
  })
  .catch((err) => {
    console.log(`%c catch: ${err}`, 'color: red')
  });

@xbcc123
Copy link

xbcc123 commented May 3, 2019

	window.Promise.prototype = {
			finally: function(callback) {
				let P = this.constructor;
				  return this.then(
				    value  => P.resolve(callback()).then(() => value),
				    reason => P.resolve(callback()).then(() => { throw reason })
				  );
			}
		}

抄了个...

@hello-chinese
Copy link

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => {
          callback();
          return value;
     },
    reason => {
        callback();
        throw reason
    }
  );
};

为什么需要Promise.resolve(callback()).then(() => value)
而不能直接执行callback, return value

@NathanHan1
Copy link

Promise.prototype.finally = function(callback) {
        return this.then(
          () => {
            callback();
          },
          () => {
            callback();
          }
        );
      };

@Arrogant128
Copy link

Arrogant128 commented Jul 17, 2019

Promise.prototype.finally = function(callback){
  const constructor = this.constructor
  return this.then(value => {
    return constructor.resolve(callback()).then(() => value)
  }),
  reason => {
    return constructor.resolve(callback()).then(() => throw reason)
  }
}

@HCLQ
Copy link

HCLQ commented Jul 19, 2019

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => {
          callback();
          return value;
     },
    reason => {
        callback();
        throw reason
    }
  );
};

为什么需要Promise.resolve(callback()).then(() => value)
而不能直接执行callback, return value

因为callback如果是个异步操作,返回promise呢.希望等callback执行完再接着执行

@pagemarks
Copy link

https://github.com/taylorhakes/promise-polyfill/blob/master/src/finally.js

finally是一个关键字,在IE低版本会引发解析错误,若兼容IE不能直接object.key语法.

Promise.prototype['finally'] = function (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);
      });
    }
  );
}

@yft
Copy link

yft commented Aug 26, 2019

看了这个问题才知道,原来 promise 的 finally 只是不管成功还是失败都会执行而已,而不会永远最后执行😂

new Promise((resolve, reject) => {
    resolve();
}).finally(() => {
    console.log('finally1');
}).then(() => {
    console.log('then');
}).finally(() => {
    console.log('finally2');
});
finally1
then
finally2

@x-shadow-x
Copy link

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};

想问下,这里先通过P缓存this.constructor,后面再通过P.resolve的方式调用是有什么目的么,为什么不是直接value => Promise.resolve(callback()).then(() => value),reason => Promise.resolve(callback()).then(() => { throw reason })~ 谢谢

@nianxiongdi
Copy link

来个简洁版的:

finally(callback) {
    return this.then(
        (res) => {
            callback();
            return res;
        },
        (err) => {
            callback();
            return err;
        }
    );
}

@SoftwareEngineerPalace
Copy link

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};

看一个多月,还是没看懂,哪位大神能逐句解释一下

@SoftwareEngineerPalace
Copy link

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};

想问下,这里先通过P缓存this.constructor,后面再通过P.resolve的方式调用是有什么目的么,为什么不是直接value => Promise.resolve(callback()).then(() => value),reason => Promise.resolve(callback()).then(() => { throw reason })~ 谢谢

同问

@liujie2019
Copy link

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};

想问下,这里先通过P缓存this.constructor,后面再通过P.resolve的方式调用是有什么目的么,为什么不是直接value => Promise.resolve(callback()).then(() => value),reason => Promise.resolve(callback()).then(() => { throw reason })~ 谢谢

我觉得是为了兼容那些自定义的Promise版本。比如说可以自己实现一个Promise叫MyPromise,像resolve和reject这些静态方法都是挂载在MyPromise上的。通过这样let P = this.constructor;写,兼容性更好吧。

@webfool
Copy link

webfool commented Nov 24, 2019

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value, () => value),
    reason => P.resolve(callback()).then(() => { throw reason }, () => {throw reason})
  );
};

如上在阮大神关于 finally 的写法中补充了一点。不知是否正确,欢迎探讨和学习。

callback 可能存在返回 promise,而该 promise 如果 reject,P.resolve 就会 reject,如果 P.resolve().then() 没有设置第二个回调,那么 this.then 的最终状态将是 reject 的状态,这与 es6 中所表现出来的 finally 的行为不一致。

如下是 es6 所表现的行为:

new Promise((resolve, reject) => resolve(111)).finally(Promise.reject(222)) // 此时 es6 表现的行为状态为 resolve(111),而非 reject(222)

@yygmind yygmind added the 异步 label Dec 16, 2019
@thxiami
Copy link

thxiami commented Jan 2, 2020

@webfool
新加的代码与规范中规定的行为不符合, 你的代码:

new Promise((resolve, reject) => resolve(111))
  .finally(Promise.reject(222)) // Promise {<resolved>: 111}

之所以最终返回的 Promise的状态为 resolve(111), 那是因为你传给finally函数的参数的不是函数类型, 而是一个Promise对象.

如果对代码进行小小修改, 就发现 @lvtraveler 实现的 finally方法与 es6 的 Promisefinally 的行为一致:

new Promise((resolve, reject) => resolve(111))
  .finally(() => Promise.reject(222)) // Promise {<rejected>: 222}

现在再解释为什么你的代码得到Promise {<resolved>: 111}的结果, 即代码是如何执行的.

规范中对这种传给finally函数的参数的不是函数类型情况的处理如下:

Promise.prototype.finally = function(onFinally) {
  if (typeof onFinally !== 'function') {
    return this.then(onFinally, onFinally)
 }
  // ...
}

那么this.then(onFinally, onFinally)又是怎么处理的呢?

对于在 then方法中传入非函数类型的参数的情况, Promise/A+规范 2.2.1.1 和 2.2.1.2规定如下:

A promise’s then method accepts two arguments:
promise.then(onFulfilled, onRejected)

  • 2.2.1 Both onFulfilled and onRejected are optional arguments:
    • 2.2.1.1 If onFulfilled is not a function, it must be ignored.
    • 2.2.1.2 If onRejected is not a function, it must be ignored.

注: 这里没有给出 ES6 规范中对于 Promise 的规定, 是因为ES6 的 Promise 行为遵循 Promise/A+ 规范, 另外我也没看过 ES6 的规范

所以代码最后相当于:

Promise.prototype.finally = function(onFinally) {
  if (typeof onFinally !== 'function') {
    return this.then()  // 注: bluebird 也是这么实现的: https://github.com/petkaantonov/bluebird/blob/master/src/finally.js#L93
 }
  // ...
}

对于 onFulfilled and onRejected 为非函数类型的情况, 具体的执行行为规范2.2.7.3 和 2.2.7.4 规定如下:

promise2 = promise1.then(onFulfilled, onRejected);

  • If onFulfilled is not a function and promise1 is fulfilled, promise2 must be fulfilled with the same value as promise1.
  • If onRejected is not a function and promise1 is rejected, promise2 must be rejected with the same reason as promise1.

根据对规范的理解, 具体的执行行为用代码表示为:

Promise.prototype.finally = function(onFinally) {
  if (typeof onFinally !== 'function') {
    return this.then(
      val => val,
      reason => {
        throw reason
      }
    )
  }
// ...
}

再回到最初你的代码, 就知道最终finally返回的 Promise的状态由new Promise.resolve(111) 这个Promise来决定

@bosens-China
Copy link

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};

看一个多月,还是没看懂,哪位大神能逐句解释一下

第一步获取构建函数,这里constructor就是获取,为什么用这个呢?主要是以前的promise实现有第三方的存在
后面返回一个then对象,then接受两个方法,这里统一用resolve接收,之后等待callback执行完成后继续返回结果和抛出异常

@Mrcxt
Copy link

Mrcxt commented May 11, 2020

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};

看一个多月,还是没看懂,哪位大神能逐句解释一下

首先是生成函数,这里构造函数就是获取,为什么用这个呢呢?主要是以前的promise实现有第三方的存在
后面返回一个对象,然后接受两个方法,这里统一用resolve接收,之后等待回调执行完成后继续返回结果和抛出异常

有一点不明白,为什么异常处理要写在then的第二个参数里,而不是用catch方法

@cutie6
Copy link

cutie6 commented May 22, 2020

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => {
          callback();
          return value;
     },
    reason => {
        callback();
        throw reason
    }
  );
};

为什么需要Promise.resolve(callback()).then(() => value)

而不能直接执行callback, return value

这里都不需要有 let P = this.constructor; 这一句了吧

@rottenpen
Copy link

Promise._finally = function(cb) {
  return this.then(value => { cb(); return value }, err => { cb(); throw err})
}

@Fan-zexu
Copy link

Fan-zexu commented Jul 8, 2020

Promise.prototype.myFinally = function (func) {
return this.then(func)
}
直接这样就好了呀

@tjwyz
Copy link

tjwyz commented Jul 16, 2020

PromiseA.prototype.finally = (fn) => {
	// 保证了fn执行在前.. 但是有点绕
	return this.then((param)=>{
		// 万一 fn reject了
		return PromiseA.resolve(fn()).then(()=>param, ()=>param);
	}, (err) =>{
		// 万一 fn reject了
		return PromiseA.resolve(fn()).then(()=>{
			throw err;
		}, ()=>{
			throw err;
		});
	})
}

@m7yue
Copy link

m7yue commented Sep 10, 2020

  1. finally 方法 , 不管 Promise 对象最后状态如何,都会执行的操作, 返回的依然是个Promise;
  2. 参数 cb 没有接收之前 Promise 的值, 只是执行 cb 并继承之前的状态
  finally (cb) {
    return this.then(
      value  => Promise.resolve(cb()).then(() => value),
      reason => Promise.resolve(cb()).then(() => { throw reason })
    );
  }

@Eleven-Ding
Copy link

	window.Promise.prototype = {
			finally: function(callback) {
				let P = this.constructor;
				  return this.then(
				    value  => P.resolve(callback()).then(() => value),
				    reason => P.resolve(callback()).then(() => { throw reason })
				  );
			}
		}

抄了个...

你这样是不是把prototype都覆盖了

@jackluson
Copy link

Promise.prototype.myFinally = async function (cb) {
  const pr = this;
  try {
    await pr;
  } finally {
    cb && cb();
  }
};

const start = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const temp = Math.round(Math.random());
      if (temp > 0.5) {
        resolve(temp);
      } else {
        reject(temp);
      }
    }, 2000);
  });
};

start()
  .then((res) => {
    console.log("res", res);
  })
  .catch((err) => {
    console.log("err", err);
  })
  .myFinally(() => {
    console.log("finally");
  });

更多解析

@XMH19970729
Copy link

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => {
          callback();
          return value;
     },
    reason => {
        callback();
        throw reason
    }
  );
};

为什么需要Promise.resolve(callback()).then(() => value) 而不能直接执行callback, return value
你这个不能处理异步情况

@SnailOwO
Copy link

看了这个问题才知道,原来 promise 的 finally 只是不管成功还是失败都会执行而已,而不会永远最后执行joy

new Promise((resolve, reject) => {
    resolve();
}).finally(() => {
    console.log('finally1');
}).then(() => {
    console.log('then');
}).finally(() => {
    console.log('finally2');
});
finally1
then
finally2

你不说,我都没仔细注意到。。。

@Yangfan2016
Copy link

Promise.prototype.finally = function (callback) {
    return this.then(
        r => Promise.resolve(callback()).then(() => r),
        e => Promise.resolve(callback()).then(() => { throw e })
    )
}

@wangsen2020
Copy link

看了这个问题才知道,原来 promise 的 finally 只是不管成功还是失败都会执行而已,而不会永远最后执行😂

new Promise((resolve, reject) => {
    resolve();
}).finally(() => {
    console.log('finally1');
}).then(() => {
    console.log('then');
}).finally(() => {
    console.log('finally2');
});
finally1
then
finally2

你不说,我都没注意到,招你这样说,这就是个普通函数而已

@xuxin666666
Copy link

这样应该没什么问题吧,直接this也不用管是引入的还是默认的,cb也简单处理了一下,也能传递数据或者错误

Promise.prototype.finally = function(cb) {
    cb = typeof cb === 'function' ? cb : () => cb
    return this.then(cb, cb)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests