即使使用了延迟对象,异步函数也不会在循环内顺序执行

时间:2015-08-21 15:14:54

标签: jquery asynchronous deferred

我有一个在循环中运行的异步函数。此函数正在创建一些带日期的HTML按钮。日期应按顺序进行。但最后日期的顺序非常随机。这是我的代码

   var getDatesAvailable = function() {
    var weekDays = masterConfigurations[0].DaysOfWeek.results;
    var advancedDays = masterConfigurations[0].DaysToShow;
    var todaysDate = new Date();
    var futureDate = new Date(todaysDate);
    futureDate.setDate(todaysDate.getDate() + advancedDays);
    var availableDates = returnFinalDates(todaysDate, futureDate, weekDays);
    var formattedDates = [];
    $(availableDates).each(function(i, e) {
        var tDate = commonOperations.pad(e.getDate(), 2);
        var tMonth = commonOperations.pad((e.getMonth() + 1), 2);
        var isoDate = e.getFullYear() + "-" + tMonth + "-" + tDate + "T00:00:00.000z";
        formattedDates.push({
            "displayDate": (tDate + "/" + tMonth),
            "dateObj": isoDate
        });
    });
    var dateButtons = "";
    var buttonDates = [];
    var schoolCode = $('#selectedSchoolAddress').data('schoolCode') || "";
    $(formattedDates).each(function(i, e) {
        var startDate = new Date(e.dateObj).toISOString();
        var endDate = e.dateObj.split("T")[0];
        endDate = (new Date(endDate + "T23:59:59.000z")).toISOString();

        commonOperations.queryList("ITFS_Transact_School_Visits", "?$filter=((ITFSVisitDate gt '" + startDate + "') and (ITFSVisitDate lt '" + endDate + "') and (ITFSVisitSchoolCode eq '" + schoolCode + "'))", false).then(function(bookings) {
            var maxBookings = masterConfigurations[0].MaxBookingsPerSession;
            var bookingsAlreadyHad = bookings.d.results.length || 0;

            if (bookingsAlreadyHad >= maxBookings) {
                dateButtons = dateButtons + '<button type="button" class="btn btn-default dateButton add10Margin" data-date="' + e.dateObj + '" disabled="disabled">' + formattedDates[i].displayDate + '</button>'
            } else {
                dateButtons = dateButtons + '<button type="button" class="btn btn-default dateButton add10Margin" data-date="' + e.dateObj + '">' + formattedDates[i].displayDate + '</button>'
            }
            $("#displayDates").html(dateButtons);
        });
    });

};

我尝试过很多方法。即使我通过传递false作为参数来指定不通过异步执行的ajax调用。但仍然没有工作。怎么控制这个?或者有没有什么可以实现使用闭包或任何东西? 我记录了循环的索引它也没有顺序随机出现。

2 个答案:

答案 0 :(得分:0)

我可能尝试的一件事是,不是将按钮列表html构建为字符串,而是创建[i,html]对的数组或{i:html}映射的对象,以及然后在做.html()时对它们进行排序并连接。它至少会将同步性作为变量去除。

修改

概念证明:http://jsfiddle.net/mkb0fyco/

function randomDeferred(){
    var deferred = $.Deferred();
    var promise = deferred.promise();

    window.setTimeout(function(){
        deferred.resolve();
    }, Math.random() * 3000 + 1000);

    return promise;
}

var dates = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];
var buttons = [];
$(dates).each(function(i,e){
    $.when(randomDeferred()).then(function(){
        buttons[i] = e;
        $('body').empty();
        $(buttons).each(function(i,e){
            if(e) // has it been set?
            {
                $('body').append('<button>' + e + '</button>');
            }
        });
    });
});

答案 1 :(得分:0)

无法保证commonOperations.queryList()返回的承诺将以与呼叫相同的顺序结算。

有多种方法可以确保按钮的添加顺序与原始数据相同。这是两个......

<强> 1。等待所有承诺结算,然后渲染按钮

var getDatesAvailable = function() {
    var weekDays = masterConfigurations[0].DaysOfWeek.results;
    var advancedDays = masterConfigurations[0].DaysToShow;
    var todaysDate = new Date();
    var futureDate = new Date(todaysDate);
    futureDate.setDate(todaysDate.getDate() + advancedDays);
    var schoolCode = $('#selectedSchoolAddress').data('schoolCode') || "";

    // Here the array returned by `returnFinalDates()` is mapped to an array of promises. 
    // Each promise will deliver a jQuery-wrapped <button>.
    var promises = returnFinalDates(todaysDate, futureDate, weekDays).map(function(i, e) {
        var tDate = commonOperations.pad(e.getDate(), 2);
        var tMonth = commonOperations.pad((e.getMonth() + 1), 2);
        var isoDate = e.getFullYear() + "-" + tMonth + "-" + tDate + "T00:00:00.000z";
        var dates = {
            "displayDate": (tDate + "/" + tMonth),
            "dateObj": isoDate
        };
        var startDate = new Date(dates.dateObj).toISOString();
        var endDate = dates.dateObj.split("T")[0];
        endDate = (new Date(endDate + "T23:59:59.000z")).toISOString();

        // Return a promise into the `promises` array.
        return commonOperations.queryList("ITFS_Transact_School_Visits", "?$filter=((ITFSVisitDate gt '" + startDate + "') and (ITFSVisitDate lt '" + endDate + "') and (ITFSVisitSchoolCode eq '" + schoolCode + "'))", false)
        .then(function(bookings) {
            // The jQuery-wrapped button returned here becomes the value delivered by the promise pushed into the `promises` array.
            return $('<button type="button" class="btn btn-default dateButton add10Margin" data-date="' + dates.dateObj + '">' + dates.displayDate + '</button>')
            .prop('disabled', (bookings.d.results.length || 0) >= masterConfigurations[0].MaxBookingsPerSession);
        });
    });

    // When all the commonOperations.queryList() promises are fulfilled, 
    // loop through the delivered values (buttons) and append them, in order, to #displayDates.
    return $.when.apply(null, promises).then(function() {
        $("#displayDates").empty();
        $.each(arguments, function(i, $button) {
            $("#displayDates").append($button);
        });
    });
};

<强> 2。每个按钮同步附加一个容器,然后将每个按钮异步附加到其容器

function(i, e) {...}形成的闭包确保每个按钮与在.map()循环的同一回合中为其创建的信号匹配。

var getDatesAvailable = function() {
    var weekDays = masterConfigurations[0].DaysOfWeek.results;
    var advancedDays = masterConfigurations[0].DaysToShow;
    var todaysDate = new Date();
    var futureDate = new Date(todaysDate);
    futureDate.setDate(todaysDate.getDate() + advancedDays);
    var schoolCode = $('#selectedSchoolAddress').data('schoolCode') || "";
    var promises = returnFinalDates(todaysDate, futureDate, weekDays).map(function(i, e) {
        var tDate = commonOperations.pad(e.getDate(), 2);
        var tMonth = commonOperations.pad((e.getMonth() + 1), 2);
        var isoDate = e.getFullYear() + "-" + tMonth + "-" + tDate + "T00:00:00.000z";
        var dates = {
            "displayDate": (tDate + "/" + tMonth),
            "dateObj": isoDate
        };
        var startDate = new Date(dates.dateObj).toISOString();
        var endDate = dates.dateObj.split("T")[0];
        endDate = (new Date(endDate + "T23:59:59.000z")).toISOString();

        var $container = $("<span/>").appendTo("#displayDates"); // A span element that is synchronously appended to the #displayDates container.

        // Now return a promise into the `promises` array.
        return commonOperations.queryList("ITFS_Transact_School_Visits", "?$filter=((ITFSVisitDate gt '" + startDate + "') and (ITFSVisitDate lt '" + endDate + "') and (ITFSVisitSchoolCode eq '" + schoolCode + "'))", false)
        .then(function(bookings) {
             $('<button type="button" class="btn btn-default dateButton add10Margin" data-date="' + dates.dateObj + '">' + dates.displayDate + '</button>')
              .prop('disabled', (bookings.d.results.length || 0) >= masterConfigurations[0].MaxBookingsPerSession)
              .appendTo($container);
              // As there's nothing more to do below, there's no point returning a value here.
        });
    });

    // When all the commonOperations.queryList() promises are fulfilled, there's nothing more to do
    // except return an aggregated promise signifying successful completion (or a failure).
    return $.when.apply(null, promises);
};

这两种方法之间的差异主要围绕异步错误的后果。

  1. 将附加所有按钮或无 - 任何单一故障都会破坏整个企业。如果一切都成功,所有按钮将在最终承诺解决后一起显示。

  2. 即使一个或多个按钮失败,也会附加成功创建的每个按钮。按钮将一次出现一个,因为个人承诺解决。

  3. 更进一步,在这两种情况下,你可以插入虚拟按钮(或其他东西)来代替任何失败,尽管代码会稍微复杂一些。