【问题标题】:How to manage chained AJAX calls with jQuery如何使用 jQuery 管理链式 AJAX 调用
【发布时间】:2013-07-23 08:44:43
【问题描述】:

我有这个网站,该网站连接到本地服务,该服务管理带有密码键盘的读卡器。使用此设备可以完成许多不同的操作。以下是其中之一的示例:

  1. 锁定设备
  2. 询问 PIN 码
  3. 释放设备

以前我是把回调链接在一起,但是现在,因为有新的操作,也使用了像“lock”和“release”这样的方法,我需要改变我的代码,以便第1步的代码并且 3 是可重复使用的。

我一直在尝试使用 jQuery Promise 来解决这个问题,但我对此很陌生,我还没有真正弄清楚它们是如何工作的。有人可以帮帮我吗?

这是我现在使用的代码示例。我已经从函数内部删除了业务逻辑,以简化示例:

var CardReader = {

    ////////////////////
    // Different possible messages to the Card Reader
    ////////////////////

    lockDevice: function() {
        this.makeAjaxCall("GET", "/LockDevice", this.lockDeviceCallback);
    },

    getPin: function() {
        this.makeAjaxCall("POST", "/getPin", this.getPinCallback);
    },

    releaseDevice: function() {
        this.makeAjaxCall("POST", "/Release", this.releaseDeviceCallback);
    },

    //////////////////
    // Callbacks for each message to the Card Reader
    //////////////////

    lockDeviceCallback: function(jqXHR, textStatus) {
        if (textStatus !== "success") { return; }
        this.getCardLogin();
    },

    getCardLoginCallback: function(jqXHR, textStatus) {
        if (textStatus !== "success") { return; }
        this.releaseDevice();
    },

    releaseDeviceCallback: function(jqXHR, textStatus) {
        if (textStatus !== "success") { return; }
        //End
    },

    ////////////////
    // Other methods
    ////////////////

    init: function() {
        // UI BIndings
        $(#button).on("click", this.logIn.bind(this));
    },

    logIn: function() {
        this.lockDevice();
    },

    makeAjaxCall: function(callType,  resource, callbackMethod) {

        $.ajax({
            type       : callType,
            url        : "http://localhost:1337" + resource,
            cache      : false,
            dataType   : "json",
            contentType: "application/json",
            context    : this,
            complete   : callbackMethod
        });
    }
};

【问题讨论】:

    标签: jquery ajax asynchronous callback promise


    【解决方案1】:

    即使我不确定在这里完全理解您的问题,这也可以满足您的需求。

    这里的重点似乎是保持 ajax 方法回调的顺序。你可以这样做:

    创建这些方法:

     _nextCall: function (deferreds, method) {
            if (deferreds.length) this._when(deferreds, method);
            else console.log(method + " SUCCESS");
        },
        _when: function (calls, method) {
            var $promise = $.when(this[calls[0]]())
            $promise.then(function () {
                calls.splice(0, 1);
                this._nextCall(calls, method);
            }, function () {
                console.log(method + " FAILED on: " + calls[0]);
            });
        },
    

    像这样使用它,例如:

    logIn: function logIn() {
            var calls = ["lockDevice", "getCardLogin", "releaseDevice"];
            this._when(calls, arguments.callee.name);
        },
        getPinOnly: function getPinOnly() {
            var calls = ["getPin"];
            this._when(calls, arguments.callee.name);
        },
    

    DEMO

    完整代码:

    var CardReader = {
    
        ////////////////////
        // Different possible messages to the Card Reader
        ////////////////////
    
        lockDevice: function () {
            return this.makeAjaxCall("GET", "/LockDevice", this.lockDeviceCallback);
        },
        getCardLogin: function () {
            return this.makeAjaxCall("POST", "/getCardLogin", this.getCardLoginCallback);
        },
        getPin: function () {
            return this.makeAjaxCall("POST", "/getPin", this.getPinCallback);
        },
    
        releaseDevice: function () {
            return this.makeAjaxCall("POST", "/Release", this.releaseDeviceCallback);
        },
    
        //////////////////
        // Callbacks for each message to the Card Reader
        //////////////////
    
        lockDeviceCallback: function (jqXHR, textStatus) {
            console.log("lockDeviceCallback");
            if (textStatus !== "success") {
                return;
            }
        },
    
        getCardLoginCallback: function (jqXHR, textStatus) {
            console.log("getCardLoginCallback");
            if (textStatus !== "success") {
                return;
            }
        },
        getPinCallback: function (jqXHR, textStatus) {
            console.log("getPinCallback");
            if (textStatus !== "success") {
                return;
            }
        },
    
        releaseDeviceCallback: function (jqXHR, textStatus) {
            console.log("releaseDeviceCallback");
            if (textStatus !== "success") {
                return;
            }
            //End
        },
    
        ////////////////
        // Other methods
        ////////////////
    
        init: function () {
            // UI BIndings
            $('#btn_login').on("click", $.proxy(this.logIn, this));
            $('#btn_getPinCallback').on("click", $.proxy(this.getPinOnly, this));
        },
        _nextCall: function (deferreds, method) {
            if (deferreds.length) this._when(deferreds, method);
            else console.log(method + " SUCCESS");
        },
        _when: function (calls, method) {
            var $promise = $.when(this[calls[0]]())
            $promise.then(function () {
                calls.splice(0, 1);
                this._nextCall(calls, method);
            }, function () {
                console.log(method + " FAILED on: " + calls[0]);
            });
        },
        logIn: function logIn() {
            var calls = ["lockDevice", "getCardLogin", "releaseDevice"];
            this._when(calls, arguments.callee.name);
        },
        getPinOnly: function getPinOnly() {
            var calls = ["getPin"];
            this._when(calls, arguments.callee.name);
        },
    
        makeAjaxCall: function (callType, resource, callbackMethod) {
    
            return $.ajax({
                type: callType,
                url: "/echo/json", // + resource,
                cache: false,
                dataType: "json",
                contentType: "application/json",
                context: this,
                success: callbackMethod
            });
        }
    };
    
    CardReader.init();
    

    【讨论】:

      【解决方案2】:

      使用功能更全面的 Promise 库。看看:https://github.com/kriskowal/q 花一点时间来理解是非常值得的,因为它极大地改善了代码的样式和可读性。基本上,您可以编写每个业务函数,使其返回一个承诺,如下所示:-

      function myBusinessFunction(params) {
          var deferred = Q.defer();
          ....
          doSomeAsyncFunction(params,function(error,result) {
              if (error) {
                 deferred.reject(new Error(error)); //pass the error on
              } else {
                 deferred.resolve(result);
              }
          });
          return deferred.promise;
      }
      

      所以这里发生的事情是为您创建的每个异步操作创建一个函数,在该函数内部创建一个 deferred 并返回它。一旦您收到超时、错误或任何结果,您就会调用 deferred.reject 或 deferred.resolve。

      通过添加到您的代码中,您可以避免回调地狱,其中您有很多嵌套函数并创建您的整体算法代码,如下所示:-

      Q.fcall(promisedStep1)
      .then(promisedStep2)
      .then(promisedStep3)
      .then(promisedStep4)
      .then(function (value4) {
          // Do something with value4
      })
      .catch(function (error) {
          // Handle any error from all above steps
      })
      .done();
      

      这与 try ... catch 过程式编程非常相似,这种编程非常熟悉,但您实际上是在处理异步进程。当你在 promise 上调用 reject 时,控制流将传递给 catch 函数,大大简化了编程。

      库中有许多附加功能和特性,因此您将能够处理并行运行的步骤等。查看文档,这是非常值得的。

      【讨论】:

        【解决方案3】:
        【解决方案4】:

        在您的 Ajax 调用中尝试 async: false。这首先完成您的 Ajax 调用,然后执行其他语句。

        makeAjaxCall: function(callType, resource, callbackMethod) {

            $.ajax({
                type       : callType,
                url        : "http://localhost:1337" + resource,
                cache      : false,
                dataType   : "json",
                contentType: "application/json",
                context    : this,
                async :false,
                complete   : callbackMethod
            });
        }
        

        【讨论】:

          【解决方案5】:
                 jQuery.support.cors = true;
          
                  $.ajax({
                      type: "POST",
                      contentType: "application/json; charset=utf-8",
                      datatype: "json",               
                      async :false,
                      url: "http://yourHost:portnumeber/ServiceName/LockDevice",
                      success:
                  lockDeviceCallback,
                      error:
                  function (XmlHttpRequest, textStatus, errorThrown) {
                      errorHandler(XMLHttpRequest);
                  }
                  });            
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-03-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-12-04
            • 2013-02-28
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多