jQuery在延迟承诺时不能按预期工作

时间:2017-02-07 20:35:07

标签: javascript jquery arrays promise deferred

所以我已经对此进行了相当多的故障排除,并且正在撞墙。在大多数情况下,我非常熟悉承诺及其工作原理,并在一些项目中使用它们。我很难完成所有承诺,可以通过Google Calendar API调用不同的日历数据,并且脚本可以计算在回调函数中使用的结果数组的长度。以下是相关代码:

(function($){
    var baseUrl = 'https://www.googleapis.com/calendar/v3/calendars/',
        apiKey = 'xxxxxxxxxxxx',
        calendarData = [],
        events = [],
        allCalendars = [],
        eventsToDisplay = 9;

    /* Get all the calendars that we have registered */
    function getCalendars() {
        return $.getJSON(ajaxurl, {action: 'wps_get_calendars'});
    }

    /*  Get the events for a calendar by the calendar ID */
    function getCalendarEvents(calendarID) {
        return $.getJSON(baseUrl + calendarID + '/events', {
            maxResults: '4',
            orderBy: 'startTime',
            timeMin: moment().format(),
            singleEvents: true,
            key: apiKey
        }).success(function(data){
            calendarData.push(data.items);
    });

    /*  Create a collection of promises */
    var promises = getCalendars().then(function(calendars){
        var deferreds = [];
        calendars.forEach(function(calendar){
            deferreds.push( 
                getCalendarEvents(calendar.googleCalendarId)
            );
        });
        return deferreds;
    });

    /*  Wait until all the promises have completed, then sort the events */
    $.when.apply($, promises).then(concatEvents);

})(jQuery);

基本上问题是在$.when的最后一次调用,因为我在等待我必须完成的一系列承诺。 $.when似乎没有工作,因为如果我尝试将calendarData数组记录到$.when回调中的控制台,它将返回一个没有计算长度的数组。我能够弄清楚如何做到这一点的唯一方法是在回调中使用setTimeout,并将其设置为大约2000毫秒,但这不是理想的,因为取决于网络连接,API可用性,等等,接收所有数据的时间可能完全不同。

就像我在控制台中看到的一样,当我尝试将结果记录在$.when回调中时,我得到了这个“无长度”数组,这个回调无法迭代,因为该剧本似乎认为它是空的:

array with no length

知道我在这里做错了吗?提前感谢您的帮助。

2 个答案:

答案 0 :(得分:0)

您的代码工作方式存在几个结构性问题。我发现的问题:

  1. 请勿使用已弃用的.success()。使用.then()
  2. 您试图从承诺中返回一系列承诺。相反,使用$.when()返回一个解析为结果数组的单一承诺。
  3. $.when()对于如何返回结果非常不满意。它将它们作为单独的参数返回,而不是作为结果数组返回(它不像ES6标准Promise.all()那样工作)。
  4. jQuery Ajax调用解析为三个参数,如果直接使用jQuery ajax promises $.when(),则会与$.when()进行非常奇怪的交互。
  5. 您依赖于更高范围的变量中的副作用,而不仅仅是使用所需数据解析promises(不需要更高范围的变量来跟踪数据)。这使得代码更加独立,可以恢复,并且不受竞争条件的限制(如果在多个地方使用)。
  6. 重组getCalendar(),以便它返回一个解析为日历数组的单一承诺,从而使其更易于使用。
  7. 这可以为您提供所需的结果,并且它可以消除您依赖的一些更高范围的变量和副作用。

    (function($){
        var baseUrl = 'https://www.googleapis.com/calendar/v3/calendars/',
            apiKey = 'xxxxxxxxxxxx',
            events = [],
            eventsToDisplay = 9;
    
        /* Get all the calendars that we have registered */
        function getCalendars() {
            return $.getJSON(ajaxurl, {action: 'wps_get_calendars'}).then(function(calendars){
                var promises = calendars.map(function(calendar) {
                    return getCalendarEvents(calendar.googleCalendarId);
                });
                return $.when.apply($, promises).then(function() {
                    // convert arguments to a single array as our resolved value
                    return [].slice.call(arguments);
                });
            });
        }
    
        /*  Get the events for a calendar by the calendar ID */
        function getCalendarEvents(calendarID) {
            return $.getJSON(baseUrl + calendarID + '/events', {
                maxResults: '4',
                orderBy: 'startTime',
                timeMin: moment().format(),
                singleEvents: true,
                key: apiKey
            }).then(function(data){
                // make resolved value be just data.items
                return data.items;
        });
    
        /*  get all calendars */
        getCalendars().then(function(calendars){
            // process array of calendars here
            console.log(calendars);
        });
    
    })(jQuery);
    

    此外,很容易对$.when()的工作方式感到困惑。以下是一些解释它的一般信息。

    $.when()无法解析为一系列结果。相反,它将每个结果作为单独的参数传递给回调。所以,如果你有三个结果,那么就这样做:

    $.when(p1, p2, p3).then(function(r1, r2, r3) {
        console.log(r1, r2, r3);
    });
    

    然后,除此之外,jQuery Ajax调用也没有解析为单个值,它解析为三个值。所以,当你传递N jQuery ajax promises to $.when()时,你得到了你的回调的N个参数,其中每个参数是一个包含三个值的数组(来自jQuery Ajax promise的三个值)。

    因此,如果要在$.when()中处理任意数量的promise结果,则必须使用传递给回调的arguments对象并迭代它。

    function processWhenResults() {
        for (var i = 0; i < arguments.length; i++) {
            // output each ajax result
            // arguments[i] is an array of results for each corresponding ajax call
            // arguments[i][0] is the actual Ajax result value
            console.log(arguments[i][0]);
        }
    }
    
    
    $.when.apply($, promises).then(processWhenResults);
    

    或者,您可以像我在上面建议的代码中那样做,并将arguments对象转换为结果数组,以便您可以在其上使用普通的数组函数。

    此外,.success()已被弃用。你不应该使用它。请改用.then()

答案 1 :(得分:-2)

Haven没和他们玩过一段时间,但是你应该能够接受它并与它一起运行。

/*  Create a collection of promises */
var control = $.Deferred();
var deferreds = [];
getcalendars().done(function(calendars){
    calendars.forEach(function(calendar){
        deferreds.push( 
            getCalendarEvents(calendar.googleCalendarId)
        );
    });
    control.resolve();
});

control.done(function(){
  /*  Wait until all the promises have completed, then sort the events */
  $.when.apply($, deferreds).done(function() {concatEvents});    })