6

I'm new to Promises and trying to load lots of results from localstorage first and if that fails, fall back on getting the data from a server. I don't want to use jquery - ignore that I'm currently using $.getJSON :-/

function loader1() {
    return new Promise(function (resolve, reject) {
        localforage.getItem("data1").then(function (value) {
            if (value !== null) {
                resolve(value);
            } else {
                $.getJSON("/myapp/data1.json").then(function (data) {
                    if (data !== null) {
                        resolve(data);
                    }
                }).catch(function (err) {
                    reject(err);
                });
            }
        });
    });
}

which is fine for one file. So then I'd

loader1().then(function(result) { 
    // we have a result
    doSomethingElse();
}).catch(function (err) {
    // problem
});

But I want to do this for multiple files, some of which are in localstorage and some of which load json from a remote source. I want to load everything, and when it's finished, perform the next step of my app.

Promise.all appears to be the thing, but I can't see how to get the results of each of the loaders; I was thinking it would be like:

Promise
    .all([loader1, loader2, loader3])
    .then(function(result1,result2,result3)) {
        ...

but this isn't how it works ... so I tried

Promise
    .all([loader1, loader2, loader3])
    .then(function(result)) {
        // var result1 = result[0];
        // var result2 = result[1];
        // var result3 = result[1];

but the results of the second are the functions not the returned values ...

Searching many sites indicated that promise systems like Q and bluebird have a spread method which looks like what I want. can I do this in plain old javascript (how?) or should I look at using a library like Q or RSVP instead?

frumbert
  • 2,323
  • 5
  • 30
  • 61
  • Possible duplicate of [Best es6 way to get name based results with Promise.all](http://stackoverflow.com/questions/35297446/best-es6-way-to-get-name-based-results-with-promise-all) – jib Oct 13 '16 at 05:31

3 Answers3

6

but the results of the second are the functions not the returned values ...

You are not resolving any value form the Promise you construct. You are also not returning any values form the succeeding .then's in the chain. You need to do the following for each of your loader functions:

function loader1() {

    var data = localforage.getItem("data1");
    
    data.then(function (data) {

        if (data !== null) {
             return data;
        } else {
            data = $.getJSON("/myapp/data1.json")

            if (data !== null) {
                return data;
            } else {
                return Error;
        }

    data.catch(function (err) {
       return (err);
    }

This ensures that a call to function loader1() will either return a resolved Promise or a rejected Promise. You can then use the solution suggested by guest271314:

Promise
.all([loader1(), loader2(), loader3()])
.then(function(result) {
  // var result1 = result[0];
  // var result2 = result[1];
  // var result3 = result[1];
 })
 .catch(function (err) {
   // problem
 });

However please be aware that a Promise executes synchronously. This means that it does not automatically wait on any asynchronous action to complete inside the Promise. You have to take care of this yourself by only resolving it once the asynchronous action has completed (typically by resolving in a callback passed to an asynchronous function).

jimp
  • 16,999
  • 3
  • 27
  • 36
rabbitco
  • 2,790
  • 3
  • 16
  • 38
  • `localforage.getItem` itself returns on a promise. Isn't this saying to resolve when localforage is called (it may not yet have resolved). That is, if `localforage` fails then fallback on getting data via xhr/elsewhere, then if all that fails reject everything. – frumbert Oct 18 '16 at 21:27
  • I have updated my answer in accordance with your information that `local forage.getItem` returns a `Promise`. – rabbitco Oct 19 '16 at 06:16
  • Also you should note that even though a `.then` automatically returns a `Promise` you still need to return something inside the callback function passed to it - otherwise the `.then` will return a `Promise` with the value `undefined` – rabbitco Oct 19 '16 at 06:20
  • Also note that the `Promise`returned from `localforage.getItem` will be a one that is already either `resolved` or `rejected` and therefore you should act upon it using `.then` as a `Promise` by design only can be `resolved` or `rejected` once. – rabbitco Oct 19 '16 at 06:24
  • This is brilliant :) Promise .all() +1 – lky May 17 '22 at 00:00
5

Call the functions

Promise
.all([loader1(), loader2(), loader3()])
.then(function(result) {
  // var result1 = result[0];
  // var result2 = result[1];
  // var result3 = result[1];
 })
 .catch(function (err) {
   // problem
 });

See also How to cache a downloaded javascript fle which is downloaded using script tag

Community
  • 1
  • 1
guest271314
  • 1
  • 15
  • 104
  • 177
0

You can use destructuring:

Promise.all([loader1(), loader2(), loader3()])
.then(function([result1, result2, result3]) {
  ...
Community
  • 1
  • 1
jib
  • 40,579
  • 17
  • 100
  • 158