如何使用jQuery管理链式AJAX调用

时间:2022-12-15 14:29:13

I have this website that connects with a local service that manages a CardReader with a PIN Pad. There are many different operations that can be completed using this device. Heres an example of one of them:

我有这个网站连接本地服务,管理带有PIN键盘的CardReader。使用此设备可以完成许多不同的操作。以下是其中一个例子:

  1. Lock the device
  2. 锁定设备
  3. Ask for PIN number
  4. 询问密码
  5. Release device
  6. 释放装置

Before I used to chain the callbacks between each other, but now, because there are new operations, that also use methods like "lock" and "release", I need to change my code, so that the code for step 1 and 3 is reusable.

之前我曾经在彼此之间链接回调,但是现在,因为有新的操作,也使用像“lock”和“release”这样的方法,我需要更改我的代码,以便第1步和第3步的代码是可重复使用的。

I have been trying to solve this with jQuery promises, but Im quite new to this, and I still havent really figured out how they work. Can someone give me a hand?

我一直试图用jQuery的承诺解决这个问题,但我对此很新,我还没有真正弄清楚它们是如何工作的。有人可以帮我一把吗?

This is an example of the code im using now. I have removed the business logic from inside the functions, to simplify the example:

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

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
        });
    }
};

5 个解决方案

#1


1  

This could fit your needs even i'm not sure to understand fully your problematic here.

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

The important point here seems to keep order on ajax method callbacks. You could do something like that:

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

Create these methods:

创建这些方法:

 _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]);
        });
    },

Use it like that e.g:

像这样使用它:

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

DEMO

COMPLETE CODE:

完整代码:

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


1  

Use a more fully featured promises library. Take a look at: https://github.com/kriskowal/q It is well worth taking a little time to understand as it vastly improves the style and readability of your code. Basically you can write each business function so it returns a promise, so something like the following:-

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

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;
}

So what is going on here is for each async operation that you do create a function, inside that create a deferred and return it. As soon as you get a timeout or an error or any result then you call deferred.reject or deferred.resolve.

所以这里发生的是每个异步操作,你创建一个函数,在里面创建一个延迟并返回它。一旦出现超时或错误或任何结果,您就会调用deferred.reject或deferred.resolve。

With that addition to your code you can then avoid callback hell where you have lots of nested functions and create your overall algorithm code something like the following:-

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

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();

This closely resembles the try ... catch procedural style programming that is so familiar but you are actually dealing with async processes. When you call the reject on the promise then control flow will pass to the catch function greatly simplifying programming.

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

There are many additional functions and features in the library so you will be able to cope with running steps in parallel and so on. Checkout the docs it is well worth it.

库中有许多其他功能和特性,因此您可以并行处理运行步骤等。查看文档是值得的。

#3


1  

Read this jquery documentation pages:

阅读此jquery文档页面:

http://api.jquery.com/category/callbacks-object/

http://api.jquery.com/category/callbacks-object/

http://api.jquery.com/category/deferred-object/

http://api.jquery.com/category/deferred-object/

#4


1  

Try async: false with your Ajax call. This first completes you Ajax call, and then executes other statements.

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

makeAjaxCall: function(callType, resource, callbackMethod) {

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


-1  

       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);
        }
        });            

#1


1  

This could fit your needs even i'm not sure to understand fully your problematic here.

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

The important point here seems to keep order on ajax method callbacks. You could do something like that:

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

Create these methods:

创建这些方法:

 _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]);
        });
    },

Use it like that e.g:

像这样使用它:

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

DEMO

COMPLETE CODE:

完整代码:

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


1  

Use a more fully featured promises library. Take a look at: https://github.com/kriskowal/q It is well worth taking a little time to understand as it vastly improves the style and readability of your code. Basically you can write each business function so it returns a promise, so something like the following:-

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

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;
}

So what is going on here is for each async operation that you do create a function, inside that create a deferred and return it. As soon as you get a timeout or an error or any result then you call deferred.reject or deferred.resolve.

所以这里发生的是每个异步操作,你创建一个函数,在里面创建一个延迟并返回它。一旦出现超时或错误或任何结果,您就会调用deferred.reject或deferred.resolve。

With that addition to your code you can then avoid callback hell where you have lots of nested functions and create your overall algorithm code something like the following:-

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

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();

This closely resembles the try ... catch procedural style programming that is so familiar but you are actually dealing with async processes. When you call the reject on the promise then control flow will pass to the catch function greatly simplifying programming.

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

There are many additional functions and features in the library so you will be able to cope with running steps in parallel and so on. Checkout the docs it is well worth it.

库中有许多其他功能和特性,因此您可以并行处理运行步骤等。查看文档是值得的。

#3


1  

Read this jquery documentation pages:

阅读此jquery文档页面:

http://api.jquery.com/category/callbacks-object/

http://api.jquery.com/category/callbacks-object/

http://api.jquery.com/category/deferred-object/

http://api.jquery.com/category/deferred-object/

#4


1  

Try async: false with your Ajax call. This first completes you Ajax call, and then executes other statements.

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

makeAjaxCall: function(callType, resource, callbackMethod) {

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


-1  

       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);
        }
        });