面试官很忙系列:Promise 的 done、finally 那些事

简介: Promaise 大家再熟悉不过了,Promise 是异步编程的一种解决方案,比传统的解决方案,回调函数和事件更合理和更强大。Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

网络异常,图片无法展示
|


前言


Promaise 大家再熟悉不过了,Promise 是异步编程的一种解决方案,比传统的解决方案,回调函数和事件更合理和更强大。Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。


ES6 的 Promise API 提供的方法不是很多,有些有用的方法可以自己部署。下面介绍如何部署两个不在 ES6 之中、但很有用的方法。done 方法和 finally 方法。finally 方法大家可能用的比较多,done 方法相对少一点,并且现在这两个方法出现在面试中的概率越来越大了,比如:


  1. done 方法实现原理是什么?你能自己实现一个吗?
  2. finally 方法运行机制手写一个看看?
  3. done、finally 方法到底谁最后执行

这个可能是一个问答题,也可能是一个看题说结果的题目。

网络异常,图片无法展示
|


几个问题都是现在问的比较多的,因为 Promise 其他的相关问题都已经被大家所熟悉了,今天我来看看这几个不被大家熟悉的问题。


1. done


如果你使用过 Promise 类库的话,你可能见过 done 方法,Promise 类库提过Promise.prototype.done ,用 done 方法来替代 then 方法。在 Promise 规范和 Promise+ 规范中并没有对 Promise.prototype.done 做任何的规范,那为什么会出现这个方法了。一切都源于那些 “消失的错误”


消失的错误


我们先回忆一下 Promise 的特点。“对象的状态不受外界影响”,“一旦状态改变,就不会再变,任何时候都可以得到这个结果”。也回忆一下 Promise 的缺点“无法取消Promise,一旦新建它就会立即执行,无法中途取消”,“当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成) ”,“如果不设置回调函数,Promise内部抛出的错误,不会反应到外部”。

看到最后一条缺点你可能明白了,Promise 不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。我们来看一个例子:


function JSONPromise(value) {
    return new Promise(function (resolve) {
        resolve(JSON.parse(value));
    });
}
// 运行示例
const string = "一个不合法的json字符串";
JSONPromise(string).then(function (object) {
    console.log(object);
}).catch(function(error){
    // => JSON.parse抛出异常时
    console.error(error);
});


由于 string 这个字符串是一个不合法的 JSON 字符串,所以会解析抛出一个错误,然后被catch 捕捉到。正常情况你写了catch 方法正常捕获,但是如果没有写或者漏写了,一旦发生异常,想要查找源头就是一个非常棘手的问题。


function JSONPromise(value) {
    return new Promise(function (resolve) {
        resolve(JSON.parse(value));
    });
}
// 运行示例
const string = "一个不合法的json字符串";
JSONPromise(string).then(function (object) {
    console.log(object);
});


这里可能例子比较简单,在实际的研发过程中 Promise 的使用肯定是比这个例子复杂得多,而且代码的异常也可能是多种多样的。但是,由于  Promise 的 try-catch 机制,这个问题可能就会在 Promise 的内部消化掉,也就是所谓的消失的错误。当然有的同学会说我每次调用进行 catch 处理不就好了,这样无疑是最好的。但是并不是每一个人都像你这样优秀😁。如果在实现的过程中出现了这个例子中的错误的话,那么进行错误排除的工作也会变得困难。


消失的错误还有一个专业名词unhandled rejection,意思就是 Rejected 时没有找到相应处理的意思。在很多 Promise 类库中对unhandled rejection都会有相应的处理。例如:


  • ypromise 在检测到 unhandled rejection 错误的时候,会在控制台上提示相应的信息。【Promise rejected but no error handlers were registered to it】
  • Bluebird 在比较明显的人为错误,即ReferenceError等错误的时候,会直接显示到控制台上。【Possibly unhandled ReferenceError. xxx】
  • 原生(Native)的 Promise 实现为了应对同样问题,提供了GC-based unhandled rejection tracking功能。该功能是在 promise 对象被垃圾回收器回收的时候,如果是 unhandled rejection 的话,则进行错误显示的一种机制。FirefoxChrome 的原生Promise都进行了部分实现。


原理实现


它的实现代码相当简单。

Promise.prototype.done = function (onFulfilled, onRejected) {
  this.then(onFulfilled, onRejected)
    .catch(function (reason) {
      // 抛出一个全局错误
      setTimeout(() => { throw reason }, 0);
    });
};


从上面代码可见,done方法的使用,可以像then方法那样用,提供FulfilledRejected状态的回调函数,也可以不提供任何参数。但不管怎样,done都会捕捉到任何可能出现的错误,并向全局抛出。如果严格一点,也可以这样写:


"use strict";
if (typeof Promise.prototype.done === "undefined") {
    Promise.prototype.done = function (onFulfilled, onRejected) {
        this.then(onFulfilled, onRejected).catch(function (error) {
            setTimeout(function () {
                throw error;
            }, 0);
        });
    };
}


小结


done并不返回 Promise 对象,所以在done 之后并不能在使用catch 。done 的错误是直接抛出去的,并不会进行 Promise 的错误处理。Promise具有强大的错误处理机制,而done则会在函数中跳过错误处理,直接抛出异常。

讲完 done 方法你已经了解到为什么会有 done 的出现,如果自己实现一个,接下来在来看看 finally 方法。


2. finally


finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。它与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。


server.listen(0)
  .then(function () {
    // run test
  })
  .finally(server.stop);


Why not .then(f, f)?


其实本质上 finally(func)与 then(func,func)类似,但是在一些关键方面有所不同:

  • 内联创建函数时,您可以传递一次,而不必被强制声明两次或为其创建变量
  • 由于没有可靠的方法来确定 Promise 是否已兑现,因此 finally 回调将不会收到任何参数。正是这种用例适用于您不关心拒绝原因或实现价值,因此不需要提供它的情况。
  • 与 Promise.resolve(2).then(() => {}, () => {}) (将使用未定义的解析)不同,Promise.resolve(2).finally(() => {}) 将用2.解决
  • 同样,与Promise.reject(3).then(() => {}, () => {})(将使用未定义的解析)不同,Promise.reject(3).finally(() => {})将被拒绝3。


原理实现


它的实现也很简单。

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是fulfilled还是rejected,都会执行回调函数callback


小结


finally 方法本质是一个 then 方法,所以在实现方法中要调用 then 方法入参是一个函数,需要在 then 方法中执行这个函数

使用 Promise.resolve 会等入参的函数执行完再返回结果,并将上一个 then 的 value 返回 reject 方法中需要抛出错误信息。


3. done、finally 方法到底谁最后执行?


在讨论这个问题之前,我们先把 Promise.prototype.finally 转换为 ES5 是什么样的。

"use strict";
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;
  }));
};


在线转换:es6console.com/babeljs.io/repl

你是不是明白了什么,要这么写的原因是在于,finally其实并不一定是这个promise链的最后一环,相对而言,其实done才是。因为finally可能之后还有thencatch等等,所以其必须要返回一个promise对象。是不是瞬间秒懂。


总结


今天对 Promise 的 done 方法和 finally 方法进行了一个介绍,也从原理的角度为大家手写了它们的实现,这两个方法看完也可以在项目中使用起来,但是注意兼容性,并不是所有地方都能使用。希望今天的文章对你有帮助。

如果你觉得写得不错,帮忙点个赞吧。


参考


目录
相关文章
|
7月前
|
存储 前端开发 JavaScript
【面试题】Promise只会概念远远不够,还需这17道题目巩固!
【面试题】Promise只会概念远远不够,还需这17道题目巩固!
|
7月前
|
存储 前端开发 JavaScript
【面试题】面试官问:如果有100个请求,你如何使用Promise控制并发?
【面试题】面试官问:如果有100个请求,你如何使用Promise控制并发?
153 0
|
7月前
|
前端开发 JavaScript API
【面试题】说说 Promise是什么?如何使用
【面试题】说说 Promise是什么?如何使用
106 0
|
7月前
|
前端开发
【面试题】吃透Promise?先实现一个再说(包含所有方法)(二)
【面试题】吃透Promise?先实现一个再说(包含所有方法)(二)
|
7月前
|
存储 运维 前端开发
【面试题】吃透Promise?先实现一个再说(包含所有方法)(一)
【面试题】吃透Promise?先实现一个再说(包含所有方法)(一)
|
7月前
|
前端开发 JavaScript
【面试题】async/await、promise和setTimeout的执行顺序
【面试题】async/await、promise和setTimeout的执行顺序
102 0
|
2月前
|
存储 Java
[Java]面试官:你对异常处理了解多少,例如,finally中可以有return吗?
本文介绍了Java中`try...catch...finally`语句的使用细节及返回值问题,并探讨了JDK1.7引入的`try...with...resources`新特性,强调了异常处理机制及资源自动关闭的优势。
25 1
|
4月前
|
Java
【Java基础面试三十九】、 finally是无条件执行的吗?
这篇文章解释了Java中的finally块的特性,即无论是否发生异常或执行了return语句,finally块都会无条件执行,除非使用System.exit()退出虚拟机。
|
4月前
|
Java
【Java基础面试四十】、在finally中return会发生什么?
文章讨论了在Java中finally块中使用return语句的问题,指出如果在finally块中使用return或throw语句,将导致try块或catch块中的相应语句失效,因为finally块中的return或throw会终止方法,之后系统不会再执行try或catch块中的代码。
|
7月前
|
存储 前端开发 JavaScript
面试官问:如果有100个请求,你如何使用Promise控制并发?
面试官问:如果有100个请求,你如何使用Promise控制并发?
395 0

热门文章

最新文章