zoukankan      html  css  js  c++  java
  • Promise 多重链式调用

    Promise对象是用于异步操作的。

    Promise的真正强大之处在于它的多重链式调用,可以避免层层嵌套回调。如果我们在第一次ajax请求后,还要用它返回的结果再次请求呢?

    使用Promise,我们就可以利用then进行「链式回调」,将异步操作以同步操作的流程表示出来。

    以下是个小Demo:

    /* e.g */
    sendRequest('test1.html', '').then(function(data1) {
        console.log('第一次请求成功, 这是返回的数据:', data1);
        return sendRequest('test2.html', data1);
    }).then(function(data2) {
        console.log('第二次请求成功, 这是返回的数据:', data2);
        return sendRequest('test3.html', data2);
    }).then(function(data3) {
        console.log('第三次请求成功, 这是返回的数据:', data3);
    }).catch(function(error) {
        //用catch捕捉前面的错误
        console.log('sorry, 请求失败了, 这是失败信息:', error);
    });

    基本用法

    上一小节我们认识了promise长什么样,但对它用到的resolverejectthencatch想必还不理解。下面我们一步步学习。

    Promise对象代表一个未完成、但预计将来会完成的操作。
    它有以下三种状态:

    • pending:初始值,不是fulfilled,也不是rejected
    • fulfilled:代表操作成功
    • rejected:代表操作失败

    Promise有两种状态改变的方式,既可以从pending转变为fulfilled,也可以从pending转变为rejected。一旦状态改变,就「凝固」了,会一直保持这个状态,不会再发生变化。当状态发生变化,promise.then绑定的函数就会被调用。
    注意:Promise一旦新建就会「立即执行」,无法取消。这也是它的缺点之一。
    下面就通过例子进一步讲解。

    /* 例3.1 */
    //构建Promise
    var promise = new Promise(function (resolve, reject) {
        if (/* 异步操作成功 */) {
            resolve(data);
        } else {
            /* 异步操作失败 */
            reject(error);
        }
    });

    类似构建对象,我们使用new来构建一个PromisePromise接受一个「函数」作为参数,该函数的两个参数分别是resolvereject。这两个函数就是就是「回调函数」,由JavaScript引擎提供。

    resolve函数的作用:在异步操作成功时调用,并将异步操作的结果,作为参数传递出去; 
    reject函数的作用:在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

    Promise实例生成以后,可以用then方法指定resolved状态和reject状态的回调函数。

    /* 接例3.1 */
    promise.then(onFulfilled, onRejected);
    
    promise.then(function(data) {
      // do something when success
    }, function(error) {
      // do something when failure
    });

    then方法会返回一个Promise。它有两个参数,分别为Promise从pending变为fulfilledrejected时的回调函数(第二个参数非必选)。这两个函数都接受Promise对象传出的值作为参数
    简单来说,then就是定义resolvereject函数的,其resolve参数相当于:

    function resolveFun(data) {
        //data为promise传出的值
    }

    而新建Promise中的'resolve(data)',则相当于执行resolveFun函数。
    Promise新建后就会立即执行。而then方法中指定的回调函数,将在当前脚本所有同步任务执行完才会执行。如下例:

    /* 例3.2 */
    var promise = new Promise(function(resolve, reject) {
      console.log('before resolved');
      resolve();
      console.log('after resolved');
    });
    
    promise.then(function() {
      console.log('resolved');
    });
    
    console.log('outer');
    
    -------output-------
    before resolved
    after resolved
    outer
    resolved

    由于resolve指定的是异步操作成功后的回调函数,它需要等所有同步代码执行后才会执行,因此最后打印'resolved',这个和例2.2是一样的道理。

     

    Promise常见问题

    经过上一章的学习,相信大家已经学会使用Promise
    总结一下创建promise的流程:

    1. 使用new Promise(fn)或者它的快捷方式Promise.resolve()Promise.reject(),返回一个promise对象
    2. fn中指定异步的处理
      处理结果正常,调用resolve
      处理结果错误,调用reject

    如果使用ES6的箭头函数,将会使写法更加简单清晰。

    这一章节,将会用例子的形式,以说明promise使用过程中的注意点及容易犯的错误。

    情景1:reject 和 catch 的区别

    • promise.then(onFulfilled, onRejected)
      onFulfilled中发生异常的话,在onRejected中是捕获不到这个异常的。
    • promise.then(onFulfilled).catch(onRejected)
      .then中产生的异常能在.catch中捕获

    一般情况,还是建议使用第二种,因为能捕获之前的所有异常。当然了,第二种的.catch()也可以使用.then()表示,它们本质上是没有区别的,.catch === .then(null, onRejected)


    情景2:如果在then中抛错,而没有对错误进行处理(即catch),那么会一直保持reject状态,直到catch了错误

    /* 例4.1 */
    function taskA() {
        console.log(x);
        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);
        
    -------output-------
    Catch Error: A or B,ReferenceError: x is not defined
    Final Task

    clipboard.png

    根据例4.1的输出结果及流程图,可以看出,A抛错时,会按照 taskA → onRejected → finalTask这个流程来处理。A抛错后,若没有对它进行处理,如例3.7,状态就会维持rejected,taskB不会执行,直到catch了错误。

    /* 例4.2 */
    function taskA() {
        console.log(x);
        console.log("Task A");
    }
    function taskB() {
        console.log("Task B");
    }
    function onRejectedA(error) {
        console.log("Catch Error: A", error);
    }
    function onRejectedB(error) {
        console.log("Catch Error: B", error);
    }
    function finalTask() {
        console.log("Final Task");
    }
    var promise = Promise.resolve();
    promise
        .then(taskA)
        .catch(onRejectedA)
        .then(taskB)
        .catch(onRejectedB)
        .then(finalTask);
        
    -------output-------
    Catch Error: A ReferenceError: x is not defined
    Task B
    Final Task

    将例4.2与4.1对比,在taskA后多了对A的处理,因此,A抛错时,会按照A会按照 taskA → onRejectedA → taskB → finalTask这个流程来处理,此时taskB是正常执行的。


    情景3:每次调用then都会返回一个新创建的promise对象,而then内部只是返回的数据

    /* 例4.3 */
    //方法1:对同一个promise对象同时调用 then 方法
    var p1 = new Promise(function (resolve) {
        resolve(100);
    });
    p1.then(function (value) {
        return value * 2;
    });
    p1.then(function (value) {
        return value * 2;
    });
    p1.then(function (value) {
        console.log("finally: " + value);
    });
    -------output-------
    finally: 100
    
    //方法2:对 then 进行 promise chain 方式进行调用
    var p2 = new Promise(function (resolve) {
        resolve(100);
    });
    p2.then(function (value) {
        return value * 2;
    }).then(function (value) {
        return value * 2;
    }).then(function (value) {
        console.log("finally: " + value);
    });
    -------output-------
    finally: 400

    第一种方法中,then的调用几乎是同时开始执行的,且传给每个then的value都是100,这种方法应当避免。方法二才是正确的链式调用。
    因此容易出现下面的错误写法:

    /* 例4.4 */
    function badAsyncCall(data) {
        var promise = Promise.resolve(data);
        promise.then(function(value) {
            //do something
            return value + 1;
        });
        return promise;
    }
    badAsyncCall(10).then(function(value) {
       console.log(value);          //想要得到11,实际输出10
    });
    -------output-------
    10

    正确的写法应该是:

    /* 改写例4.4 */
    function goodAsyncCall(data) {
        var promise = Promise.resolve(data);
        return promise.then(function(value) {
            //do something
            return value + 1;
        });
    }
    goodAsyncCall(10).then(function(value) {
       console.log(value);
    });
    -------output-------
    11

    情景4:在异步回调中抛错,不会被catch

    // Errors thrown inside asynchronous functions will act like uncaught errors
    var promise = new Promise(function(resolve, reject) {
      setTimeout(function() {
        throw 'Uncaught Exception!';
      }, 1000);
    });
    
    promise.catch(function(e) {
      console.log(e);       //This is never called
    });

    情景5: promise状态变为resovereject,就凝固了,不会再改变

    console.log(1);
    new Promise(function (resolve, reject){
        reject();
        setTimeout(function (){
            resolve();            //not called
        }, 0);
    }).then(function(){
        console.log(2);
    }, function(){
        console.log(3);
    });
    console.log(4);
    
    -------output-------
    1
    4
    3

    五 结语




  • 相关阅读:
    【python 待做】
    【python 第13日】网络通信socket socketserver
    【python第11日】自动生成项目目录 安全插入本文件夹下文件,即使别人引用也可以
    【python 第12日】 except异常
    【python 第10日】打飞机的小游戏 pygame
    【python 第9日】上下文 类装饰器 元类 属性描述符扩展
    Python(2.7)-字符串
    Python(2.7)-字典(dict)
    Python(2.7)-列表(list)
    mysql linux centos yum 安装
  • 原文地址:https://www.cnblogs.com/sweet-ice/p/10582925.html
Copyright © 2011-2022 走看看