简体   繁体   中英

JQuery / JavaScript callbacks, modal dialog

I've tried to put this in generic terms to make it easier to understand. Please understand that it's the concept, not the specific example that I'm trying to overcome.

OK, so I have a bit of JavaScript that makes a call to a server to get an object, let's say an order. The order has properties including an array of order item IDs. I then want to loop through said IDs and get the order item records and return them in an array, like this:

function GetOrderItems(orderId){
    var orderItemIDs = GetOrderItemIDs(orderId); // array of order item IDs
    var orderItems = [];
    $.each(orderItemIDs, function(){
        var orderItem = GetOrderItem(this); // ignore this function's implementation
        orderItems.push(orderItem);
    });
    return orderItems;
}

The issue is that there could be quite a few order items, but I have to get them from the server 1 at a time (don't ask, this won't change). So, I want to let them know (using a jQuery UI modal dialog) how many orders there are so they have an idea of how long it will take. So, I try to inject the line noted below:

function GetOrderItems(orderId){
    var orderItemIDs = GetOrderItemIDs(orderId); // array of order item IDs
    var orderItems = [];
    $("#modalDiv").dialog({title: orderItemIDs.length + " order items to get."});
    $.each(orderItemIDs, function(){
        var orderItem = GetOrderItem(this); // ignore this function's implementation
        orderItems.push(orderItem);
    });
    return orderItems;
}

The problem is that dialog shows, but only when everything is all complete. I learned from a previous question that you could use setTimeout to get the modal to show, but how do I return the array that I'm building?

function GetOrderItems(orderId){
    var orderItemIDs = GetOrderItemIDs(orderId); // array of order item IDs
    var orderItems = [];
    $("#modalDiv").dialog({title: orderItemIDs.length + " order items to get."});
    setTimeout(function(){
        $.each(orderItemIDs, function(){
            var orderItem = GetOrderItem(this); //ignore this function's implementation
            orderItems.push(orderItem);
        });
        return orderItems;
    },0);
}

I've also doing a second setTimeout function to show progress, but it goes straight to the last one and none of the functions run, like this:

function GetOrderItems(orderId){
    var orderItemIDs = GetOrderItemIDs(orderId); // array of order item IDs
    var orderItems = [];
    $("#modalDiv").dialog({title: orderItemIDs.length + " order items to get."});
    setTimeout(function(){
        $.each(orderItemIDs, function(){
            setTimeout(function(){
                var orderItem = GetOrderItem(this); //ignore this function's implementation
                orderItems.push(orderItem);
            },500);
        });
        return orderItems;
    },0);
}

Thanks in advance!

You'll have to do it asynchronously. There's no other way to get the UI to refresh. See this related question . That means your calling code will have to change from:

var orderItems = GetOrderItems(orderId);
// do something with orderItems

To:

GetOrderItems(orderId, function (orderItems) {
    // do something with orderItems
})

Presumably, you've only been able to get away with this synchronous code by making your server requests synchronous, thus tying up the browser, etc. If you are going to take the plunge into asynchronocity, you might as well take advantage of all the potential benefits. For example, your modal could be updated to indicate how many orders remain to be downloaded:

function GetOrderItems(orderId, callback){
    var orderItemIDs = GetOrderItemIDs(orderId); // array of order item IDs
    var orderItems = [];
    var orderItemCount = orderItemIDs.length;
    $("#modalDiv").dialog({title: "<span id=\"orderItemRemaining\">" + orderItemCount + "</span> order items to get."});
    $.each(orderItemIDs, function(){
        $.get("/getOrderItem?orderId=" + this, function (orderItem) {
            orderItems.push(orderItem);
            var remaining = orderItemCount - orderItems.length;
            $("#orderItemRemaining").text(remaining);
            if (!remaining) {
                callback(orderItems);
            }
        });
    });
}  

Edit: To limit concurrent requests, don't use $.each() . Instead start just the number of concurrent requests that you want, and then initiate the rest of the requests in the request's success handler:

function GetOrderItems(orderId, callback){
    var orderItemIDs = GetOrderItemIDs(orderId); // array of order item IDs
    var orderItems = [];
    var orderItemCount = orderItemIDs.length;
    $("#modalDiv").dialog({title: "<span id=\"orderItemRemaining\">" + orderItemCount + "</span> order items to get."});
    var maxConcurrent = 3;
    for (var i = 0; i < maxConcurrent; i++) {
        getOrderItem(orderItemIDs.shift());
    }
    function getOrderItem(orderItemID) {
        $.get("/getOrderItem?orderId=" + orderItemID, function (orderItem) {
            orderItems.push(orderItem);
            var remaining = orderItemCount - orderItems.length;
            $("#orderItemRemaining").text(remaining);
            if (remaining) {
                getOrderItem(orderItemsIDs.shift());
            }
            else
                callback(orderItems);
            }
        });
    }
}  

If you are showing the dialog prior to the individual server trips it makes no sense unless you are expecting an action from the user (an OK button for example). This is because your code following the dialog() call will continue to execute regardless of the dialog's effect.

If you want to notify them of progress, you have to update the dialog each time a server request is made.

In this case, the function GetOrderItem(this) cannot be ignored. If it is an AJAX call, the rest of its parent method GetOrderItems() will continue to execute before the AJAX calls are complete.

So you would have to do something like this:

var someGlobalCounter = 0;

function GetOrderItem(item) {    // gets called X number of times
    $.ajax({
        ...
        success: function(retrievedItem) {
            someGlobalCounter++;
            $('#modalDiv').html(someGlobalCounter + ' records retrieved so far...');
            orderItems.push(retrievedItem);
        }
    });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM