2017-02-07 149 views
-1

因此,我已经做了很多这方面的疑难解答,我的头撞墙。大多数情况下,我对承诺及其工作方式非常熟悉,并已将其用于少数项目。我遇到了让我所有的承诺,在使从谷歌日历API和脚本来计算结果数组的长度回调函数来使用不同的日历数据多次打电话给做完了一点小麻烦。下面是相关代码:jQuery当不按预期延期承诺

(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似乎没有工作,因为如果我尝试在$.when回调中将calendarData数组记录到控制台,它将返回一个没有计算长度的数组。我已经能够找出如何做到这一点的唯一方法是在回调使用setTimeout,并将其设置为围绕2000毫秒,但这并不是理想的,因为这取决于网络连接性,API可用性等等,接收所有数据的时间可能完全不同。

正如我所看到的在控制台的一个想法,我得到这个“长少”阵列时,我尝试登录,结果在$.when回调,无法通过,因为脚本似乎被迭代认为这是空:

array with no length

任何想法,我做错了什么吗?预先感谢您的帮助。

+0

1)deferred.then返回一个新的承诺,而不是你返回的数组。 2)在解决getcalendars后,您将无法访问该数组。 3)将您的承诺数组放在.then的范围之外,然后使用getcalendars.then填充它。使用另一个外部延迟,你将从getcalendars.after中解析出最终的promise后推送到数组,然后用它来执行.apply到数组 –

+0

'$ .when()'实际上是脑死亡,使用时更是如此用jQuery Ajax调用。它不解析为数组。它解析为N个单独的参数,每个参数都可能是一个数组。为了处理任意数量的结果,你必须在'.when(...)。then(fn)''中处理传递给'fn'的'arguments'对象。研究jQuery文档中的'.when()'示例,特别是在使用Ajax调用时。 – jfriend00

回答

0

你必须在你的代码是如何工作的若干结构性问题。我发现的问题:

  1. 请勿使用已弃用的.success()。使用.then()
  2. 你试图从一个承诺回报承诺的数组。相反,使用$.when()那里返回解析结果的阵列中的单个承诺。
  3. $.when()在它如何返回结果方面非常好用。它返回它们作为单独的参数,而不是作为结果的阵列(它不象标准ES6确实Promise.all()工作)。
  4. 如果您直接使用jQuery ajax承诺$.when(),jQuery Ajax调用将解析为三个参数并与$.when()进行非常奇怪的交互。
  5. 您在更高范围的变量中依赖副作用,而不仅仅是解析所需数据的承诺(不需要更高范围的变量来跟踪数据)。这使得代码更大量的自包含的,可重复使用且不受竞争条件(如果不止一个地方使用)。
  6. 重组getCalendar()所以它返回一个单一的承诺,解析为一个日历数组,因此使它更简单的使用。

这应该让你得到你想要的结果,并且它可以摆脱一些你所依赖的更高范围的变量和副作用。

(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承诺$.when(),你得到N个参数给你的回调函数,其中每个参数都是一个由三个值组成的数组(jQuery Ajax承诺的三个值)。


所以,如果你想处理在$.when()承诺结果任意数量的,你必须使用传递给你的回调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); 

或者,你可以像我一样在我的推荐代码上方和转换参数反对的结果数组,所以你可以在上面正常使用阵列功能。


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

+0

为什么downvote?请解释。 – jfriend00

+0

增加了关于编码逻辑结构问题的重要附加信息。 – jfriend00

+0

@brianjohnhanna - 这是否为你解释事情并回答你的问题? – jfriend00

-2

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

/* 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}); }) 
+1

这是如何解决问题的? – jfriend00

+0

因为它确保他的所有承诺在他尝试访问它们之前都完好无损。时间的设置只在所有的承诺都在数组后完成。而deferreds []现在处于可访问的范围内。 –