0

I'm trying to load a series of templates into an array and having some trouble with jQuery's when method (based on this answer)

var files = [/*array of file names*/];
var templates = [];
files.forEach(function(file){
    templates.push(
        $.get('/temps/' + file + '.hbs').then(function(temp) {
            console.log('done loading', temp);
        })
    )
});
$.when.apply($, templates).then(function() {
    console.log(arguments); //"undefined"x10
});

Why doesn't giving the array of promises to when produce an array of my ten templates? What's gone wrong here?

Edit: Apparently, discarding the then method within each get gets me a little closer:

templates.push($.get('/temps/' + file + '.hbs'));

However I'm curious when I can't use then here, and also when

templates.push($.get('/temps/' + file + '.hbs')[0]) 

still gives me an array of undefineds. The first element in the returned array is the returned data. Why won't pushing that first element work?

Community
  • 1
  • 1
1252748
  • 14,597
  • 32
  • 109
  • 229

2 Answers2

2

Because the promises in the array are promises for undefined, not promises for your templates. You'll need to use

$.get('/temps/' + file + '.hbs').then(function(temp) {
    console.log('done loading', temp);
    return temp;
//  ^^^^^^
})

to get a promise for the template, otherwise it resolve with the implicit return value undefined. Remember that then returns a new promise for the result of the callback, not the original promise.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thank you. Can you see my edit? Is this more correct? – 1252748 Apr 04 '16 at 14:54
  • This is working pretty well. However I'm trying to return this data to another function. `$.when.apply($, templates).then(function() { return arguments });` and store the data in another variable. So, eg, all the ajax would be in a function `doAjax()` and I trying to do `var templatesFromAjax = doAjax()`, but I get undefined when on the next line I do `console.log(templatesFromAjax); – 1252748 Apr 04 '16 at 15:06
  • Yes, not using `then` at all is correct as well. Using `[0]` doesn't make sense, as promises don't have such a property. – Bergi Apr 04 '16 at 15:13
  • You can use `.then(function(){ return $.map(arguments, function(args) { return args[0]; }); })` to get an array of ajax results. `jQuery.when` is the horror when compared with promises with multiple return values. – Bergi Apr 04 '16 at 15:17
  • "horror"? Do you recommend something else? – 1252748 Apr 04 '16 at 15:17
  • You can also use [the standard function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) for real promises and [dodge jQuery deferreds completely](http://stackoverflow.com/a/31327725/1048572) :-) – Bergi Apr 04 '16 at 15:19
  • Sadly the browsers I need to support preclude the use of native promises. Though `all` would be lovely. I find jQuery promise methods to be a bit confusing to be honest. Do you have time to do a quick chat so I can show you a bit what I'm trying to do? – 1252748 Apr 04 '16 at 15:21
  • You can also use [any of these libraries](https://promisesaplus.com/implementations) as a polyfill :-) – Bergi Apr 04 '16 at 15:24
  • Hehe, I think I would meet some resistance when importing a promise library just do load a few templates. But then again it probably would have saved me a lot of time this morning! :) – 1252748 Apr 04 '16 at 15:28
  • Where I am right now is that I push all of the `gets` into an array, then return the array. Then in the function that calls it, I have a `forEach`. I would like to do something like `getTemplates().then(function(temps){/*buildPage or something*/});` but I'm getting `getTemplates.then is not a function`. however the `getTemplates.forEach` prints out the entire promise object, and I can access `responseText` which holds my value. But pushing that into another array seems overcomplicated. – 1252748 Apr 04 '16 at 15:29
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/108198/discussion-between-thomas-and-bergi). – 1252748 Apr 04 '16 at 15:37
  • Thank you for your help this morning. One thing I didn't get about the final `.then(function() { return $.map(arguments, function(args) { return args[0]; }); });` function is how `$.map` works. I would think that `arguments` would be the initial array-like object it is iterating over. However, when I put a breakpoint in the console on its `return` statements, I see that `arguments` is different each time it loops. What's happening there? – 1252748 Apr 04 '16 at 21:49
  • 1
    @thomas: Probably you've got the `arguments` of the `map` callback in the scope view, not the `arguments` of the `then` callback. – Bergi Apr 04 '16 at 22:10
  • I bet that is it exactly. Cheers. – 1252748 Apr 04 '16 at 23:14
-1

You're calling then which returns another promise. If you just want a "side effect" use done instead.

blockhead
  • 9,655
  • 3
  • 43
  • 69