0

I'm trying to load a bunch of resources async using promises with Jimp.js. The loading code is a disaster trying to chain it all, and I need a cleaner solution.

What I came up with was below. This obviously doesn't do anything, because it's junk code, but I need to know if there was a failure loading any of the resources, and I need to know when they completed.

function doSomething(asdf) {
  return new Promise((resolve, reject) => {
    //console.log("It is done.");
    // Succeed half of the time.
    var x = Math.random();    
    if (x > .5) {
      resolve(["SUCCESS",asdf,x])
    } else {
      reject(["Failure",asdf,x])
    }
  });
}



func();

function func() {
    //Imagine a is actually an image loaded by doSomething
    var a=null;  doSomething("1").then(function (data) {a = data;},
            (err) => {throw new err;});
    //Imagine b is a font resource.
    var b=null;  doSomething("2").then(function (data) {b = data;},
            (err) => {throw new err;});

    Promise.all([a, b]).then(function() {
            console.log(a);
            console.log(b);
            //Then here I expect everything to be correct, and continue on with the next function.
    }, 
    (err) => {console.log('Oops:' + err);}).
     catch( (err) => {console.log('Oops:' + err);});
}

For some reason, this never outputs "Oops".

Here is a fail output:

[ 'SUCCESS', '1', 0.756461151774289 ] null

What am I missing here?

Update

I took part of an answer I received and changed it so that it behaves exactly as I wanted:

function func() {
    var a=doSomething("1").then(function (data) {a = data;});
    var b=doSomething("2").then(function (data) {b = data;});

    Promise.all([a, b]).then(function() {
            console.log(a);
            console.log(b);
    }, 
    (err) => {console.log('Reject:' + err);});
}

Update

Here is the actual code I'm using that's working great now:

LoadResources() {
    var ps = [];
    console.log("Loading now");

    ps.push(jimp.read(this.ipath+"c4box.png").then(function (image) {obj.imBox = image;}));
    ps.push(jimp.read(this.ipath+"red.png").then(function (image) {obj.imRed = image;}));
    ps.push(jimp.read(this.ipath+"green.png").then(function (image) {obj.imGreen = image;}));
    ps.push(jimp.read(this.ipath+"top.png").then(function (image) {obj.imTop = image;}));
    ps.push(jimp.read(this.ipath+"bot.png").then(function (image) {obj.imBot = image;}));
    ps.push(jimp.loadFont(jimp.FONT_SANS_32_WHITE).then(function (font) {obj.imFont = font;}));

    Promise.all(ps).then( () => {
            obj.loaded = true;
            obj.imBg = new jimp(512, 576, function (err, image) { });
            console.log("Actually loaded now.");                       
            obj.StartGame();
    });
    console.log("Loading commands complete");            
    }
CharlesW
  • 625
  • 6
  • 14

3 Answers3

2

You cannot use those a and b variables for the images. (See here for the values that would be passed into Promise.all). You need to use variables for the promise objects that doSomething() returns. The images will only be available inside the then callback - Promise.all creates a promise that fulfills with an array of the results:

function func() {
    // aPromise is a promise for an image loaded by doSomething
    var aPromise = doSomething("1");
    // bPromise is a promise for a font resource.
    var bPromise = doSomething("2");

    Promise.all([aPromise, bPromise]).then(function([a, b]) {
//                                                  ^^^^^^
        console.log(a);
        console.log(b);
        // Then here I expect everything to be correct, and continue on with the next function.
    }, (err) => {
        console.log('Oops:' + err);})
    });
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Alternatively, if the data needs to be formatted, or only specific parts need to be extracted, you can keep the `.then()` on the `.doSomething()` call, like so: `aPromise = doSomething("1").then(function (data) { return data.subData; }`. The important piece is the `return` statement inside the `.then()`, so it can continue to be chained – mhodges May 02 '18 at 18:58
  • Thanks, I changed it to var a = doSomething()... and it's perfect. I will update the question for the future. – CharlesW May 02 '18 at 19:02
  • @CharlesW No, don't use `.then(function (data) {a = data;});` – Bergi May 02 '18 at 19:07
  • The reason that I did that was that *a* and *b* are class variables for storing images for later use. This is a persistent project in node.js. Could you further explain why I should not? – CharlesW May 02 '18 at 19:20
  • @CharlesW Those variables are useless because you never know when you can already use them or not (if the promise was not fulfilled (yet)). Instead, store the promise object itself on your class instance. – Bergi May 02 '18 at 19:38
  • @CharlesW Or if it is about constructing/initialising the `obj`, just do that inside the `then` callback on the `all` promise. You won't use the `obj` while it is incomplete anyway. – Bergi May 02 '18 at 19:40
  • @Bergi Yeah, it's about getting the object back. You can see the final code in the last update of the question. – CharlesW May 02 '18 at 20:20
  • @CharlesW Yes, but you really should use `Promises.all([read("boxpath"), read("redpath"), read("greenpath"), …]).then([box, red, green, …]) => { obj.imBox = box; obj.imRed = red; obj.imGreen = green; … })`. And preferably also put the `var obj = new …` inside that callback – Bergi May 02 '18 at 20:33
  • I disagree. I want the whole object contained in one line of code. I don't want to have to remember to type it in 3 places. I'll be adding objects. It's supposed to be readable code and easy to maintain. Also, the new object() in there is instant, I can't add it to the array, it doesn't support that in my version. – CharlesW May 07 '18 at 04:22
1
  Promise.all([a, b])

Cause a and b are null as you set them to null. Therefore Promise.all won't wait at all, it will resolve one tick afterwards, and as a and b get resolved / rejected very fast, that might has happened before and a / b gets set before it reaches

 console.log(a)

which will log the right results sometimes, but thats based on chance.

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
1

Promise.all returns a promise and this promise contains the result of the previous promises

Promise.all([ doSomething('1'), doSomething('2')])
  .then(results => {
    // results is an array which contains the result of the previous promises
    const [a, b] = results
  }).catch(err => console.log('Oops:' + err))
Ormaz
  • 46
  • 5