整理Promise提供的各种方法和错误处理方法。
1.Promise.resolve()
1.1 Promise.resolve()可以看成是new Promise()的快捷方式。Promise.resolve(42)
可以看成以下代码的语法糖:
new Promise(function(resolve){
resolve(42);
});
可以用then方法调用
Promise.resolve(42).then(function(value){
console.log(value);
});
1.2
Promise.resolve
方法另一个作用就是将 thenable 对象转换为promise对象。
ES6 Promises里提到了Thenable这个概念,简单来说它就是一个非常类似promise的东西。就像我们有时称具有 .length
方法的非数组对象为Array like一样,thenable指的是一个具有 .then
方法的对象。
这种将thenable对象转换为promise对象的机制要求thenable对象所拥有的 then
方法应该和Promise所拥有的 then
方法具有同样的功能和处理过程,在将thenable对象转换为promise对象的时候,还会巧妙的利用thenable对象原来具有的 then
方法。
到底什么样的对象能算是thenable的呢,最简单的例子就是 jQuery.ajax(),它的返回值就是thenable的。
因为jQuery.ajax()
的返回值是 jqXHR Object 对象,这个对象具有 .then
方法。
// 将thenable对象转换promise对象
var promise = Promise.resolve($.ajax('/json/comment.json'));// => promise对象
promise.then(function(value){
console.log(value);
});
2.Promise.reject()
Promise.reject(error)
是和 Promise.resolve(value)
类似的静态方法,是 new Promise()
方法的快捷方式。
比如 Promise.reject(new Error("出错了"))
就是下面代码的语法糖形式。
new Promise(function(resolve,reject){
reject(new Error("出错了"));
});
这段代码的功能是调用该promise对象通过then指定的 onRejected
函数,并将错误(Error)对象传递给这个 onRejected
函数。
Promise.reject(new Error("BOOM!")).catch(function(error){
console.error(error);
});
它和Promise.resolve(value)
的不同之处在于promise内调用的函数是reject而不是resolve,这在编写测试代码或者进行debug时,说不定会用得上。
3.链式调用的秘密
我们都了解一个很简单的 then → catch 的例子,如果我们将方法链的长度变得更长的话,那在每个promise对象中注册的onFulfilled和onRejected将会怎样执行呢?
promise chain - 即方法链越短越好。 在这个例子里我们是为了方便说明才选择了较长的方法链。
我们先来看看下面这样的promise chain。
function taskA() {
console.log("Task A");
}
function taskB() {
console.log("Task B");
}
function onRejected(error) {
console.log("Catch Error: A or B", error);
}
function finalTask() {
console.log("Final Task");
}
var promise = Promise.resolve();
promise
.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
上面代码中的promise chain的执行流程,如果用一张图来描述一下的话,像下面的图那样。
promise-then-catch-flow.png
Task A产生异常的例子
Task A 处理中发生异常的话,会按照TaskA → onRejected → FinalTask 这个流程来进行处理。
promise taska rejected flow将上面流程写成代码的话如下所示。
function taskA() {
console.log("Task A");
throw new Error("throw Error @ Task A")
}
function taskB() {
console.log("Task B");// 不会被调用
}
function onRejected(error) {
console.log(error);// => "throw Error @ Task A"
}
function finalTask() {
console.log("Final Task");
}
var promise = Promise.resolve();
promise
.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
执行这段代码我们会发现 Task B 是不会被调用的。
4.Promise.catch()
这里我们再说一遍,实际上 Promise.catch()
只是 promise.then(undefined, onRejected);
方法的一个别名而已。 也就是说,这个方法用来注册当promise对象状态变为Rejected时的回调函数。
这两种方法有何区别呢?
function throwError(value) {
// 抛出异常
throw new Error(value);
}
// <1> onRejected不会被调用
function badMain(onRejected) {
return Promise.resolve(42).then(throwError, onRejected);
}
// <2> 有异常发生时onRejected会被调用
function goodMain(onRejected) {
return Promise.resolve(42).then(throwError).catch(onRejected);
}
// 运行示例
badMain(function(){
console.log("BAD");
});
goodMain(function(){
console.log("GOOD");
});
在上面的代码中, badMain 是一个不太好的实现方式(但也不是说它有多坏), goodMain 则是一个能非常好的进行错误处理的版本。
为什么说 badMain 不好呢?,因为虽然我们在 .then 的第二个参数中指定了用来错误处理的函数,但实际上它却不能捕获第一个参数 onFulfilled 指定的函数(本例为 throwError )里面出现的错误。
也就是说,这时候即使 throwError 抛出了异常,onRejected 指定的函数也不会被调用(即不会输出"BAD"字样)。
与此相对的是, goodMain 的代码则遵循了 throwError→onRejected 的调用流程。 这时候 throwError 中出现异常的话,在会被方法链中的下一个方法,即 .catch 所捕获,进行相应的错误处理。
.then 方法中的onRejected参数所指定的回调函数,实际上针对的是其promise对象或者之前的promise对象,而不是针对 .then 方法里面指定的第一个参数,即onFulfilled所指向的对象,这也是 then 和 catch 表现不同的原因。
这里我们又学习到了如下一些内容:
-
使用
promise.then(onFulfilled, onRejected)
的话,在onFulfilled
中发生异常的话,在onRejected
中是捕获不到这个异常的。 -
在
promise.then(onFulfilled).catch(onRejected)
的情况下,then
中产生的异常能在.catch
中捕获
我们需要注意如果代码类似 badMain
那样的话,就可能出现程序不会按预期运行的情况,从而不能正确的进行错误处理。
网友评论