详解ES6 Promise

作者: _于易 | 来源:发表于2018-03-26 12:11 被阅读55次

    整理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 中捕获

    • .then.catch 在本质上是没有区别的,需要分场合使用。

    我们需要注意如果代码类似 badMain 那样的话,就可能出现程序不会按预期运行的情况,从而不能正确的进行错误处理。

    相关文章

      网友评论

        本文标题:详解ES6 Promise

        本文链接:https://www.haomeiwen.com/subject/yyizqftx.html