这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战git
Promaise 你们再熟悉不过了,Promise 是异步编程的一种解决方案,比传统的解决方案,回调函数和事件更合理和更强大。Promise
,简单说就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。es6
ES6 的 Promise API 提供的方法不是不少,有些有用的方法能够本身部署。下面介绍如何部署两个不在 ES6 之中、但颇有用的方法。done 方法和 finally 方法。finally 方法你们可能用的比较多,done 方法相对少一点,而且如今这两个方法出如今面试中的几率愈来愈大了,好比:github
这个多是一个问答题,也多是一个看题说结果的题目。面试
这几个问题都是如今问的比较多的,由于 Promise 其余的相关问题都已经被你们所熟悉了,今天我来看看这几个不被你们熟悉的问题。编程
若是你使用过 Promise 类库的话,你可能见过 done 方法,Promise 类库提过Promise.prototype.done
,用 done 方法来替代 then 方法。在 Promise 规范和 Promise+ 规范中并无对 Promise.prototype.done 作任何的规范,那为何会出现这个方法了。一切都源于那些 “消失的错误” 。json
咱们先回忆一下 Promise 的特色。“对象的状态不受外界影响”,“一旦状态改变,就不会再变,任什么时候候均可以获得这个结果”。也回忆一下 Promise 的缺点“没法取消 Promise
,一旦新建它就会当即执行,没法中途取消”,“当处于 Pending
状态时,没法得知目前进展到哪个阶段(刚刚开始仍是即将完成) ”,“若是不设置回调函数, Promise
内部抛出的错误,不会反应到外部”。promise
看到最后一条缺点你可能明白了,Promise 无论以then
方法或catch
方法结尾,要是最后一个方法抛出错误,都有可能没法捕捉到(由于Promise内部的错误不会冒泡到全局)。咱们来看一个例子:babel
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
方法正常捕获,可是若是没有写或者漏写了,一旦发生异常,想要查找源头就是一个很是棘手的问题。markdown
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
处理不就行了,这样无疑是最好的。可是并非每个人都像你这样优秀😁。若是在实现的过程当中出现了这个例子中的错误的话,那么进行错误排除的工做也会变得困难。dom
消失的错误还有一个专业名词unhandled rejection,意思就是 Rejected 时没有找到相应处理的意思。在不少 Promise 类库中对unhandled rejection都会有相应的处理。例如:
它的实现代码至关简单。
Promise.prototype.done = function (onFulfilled, onRejected) {
this.then(onFulfilled, onRejected)
.catch(function (reason) {
// 抛出一个全局错误
setTimeout(() => { throw reason }, 0);
});
};
复制代码
从上面代码可见,done
方法的使用,能够像then
方法那样用,提供Fulfilled
和Rejected
状态的回调函数,也能够不提供任何参数。但无论怎样,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 方法。
finally
方法用于指定无论 Promise 对象最后状态如何,都会执行的操做。它与done
方法的最大区别,它接受一个普通的回调函数做为参数,该函数无论怎样都必须执行。
server.listen(0)
.then(function () {
// run test
})
.finally(server.stop);
复制代码
.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 方法中须要抛出错误信息。
在讨论这个问题以前,咱们先把 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
可能以后还有then
和catch
等等,因此其必需要返回一个promise
对象。是否是瞬间秒懂。
今天对 Promise 的 done 方法和 finally 方法进行了一个介绍,也从原理的角度为你们手写了它们的实现,这两个方法看完也能够在项目中使用起来,可是注意兼容性,并非全部地方都能使用。但愿今天的文章对你有帮助。
若是你以为写得不错,帮忙点个赞吧。