1

I am new to Promises and I'm having a little trouble with the concept of waiting for a nested promise to finish all executions before resolving the original promise.

Original Code

function getSomething(text) {
    return new Promise(function (resolve, reject) {
        getElse(text).then(function (items) {
            if (items.length !== 0) {

                /* Stuff here */

                getElseElse(box).then(function (moreItems) {

                    /* Stuff here */

                    return array;
                }.then(function (array) {
                    var promise = new Promise(function (resolve, reject) {
                        /* anotherFunction() is asynchronous */
                        result = anotherFunction(array); <-- Spot 1
                    });
                    promise.then(function () { });
                });

                return resolve(result); <--- Spot 2

            }
            else {
                return resolve(null);
            }
        });
    });
};

Updated Code - better but still not fully working like I want.

function getSomething(text) {
    return getElse(text).then(function (items) {
        if (items.length !== 0) {

            /* Stuff here */

            return getElseElse(box).then(function (moreItems) {

                /* Stuff here */

                return array;
            }).then(anotherFunction);

        } else {
            return null;
        }
    });
}

Then, inside of the individual View page, I have this:

getSomething(text).then(function (result) {
    /* Do something here using result */
    /* But result is undefined and I think still pending */
});

I have optimized the original function using Thomas's help but the original call inside the view still seems to be continuing before result is formulated.

I want getSomething() to full complete and return result before I execute the code inside of the .then that is inside the view. So far, that is not being accomplished.

I have found a couple posts that have pointed me in what I think is the correct direction of Promise.all but I can't really seem to get anywhere with that information so I was hoping someone could help explain it for my specific situation.

The posts that have helped are:

  1. Promise Chain not waiting for promises to resolve before ending
  2. Wait until nested promises resolve
  3. Wait for promises inside Promise.all to finish before resolving it

Solution

The original issue was solved by Thomas in the marked answer. The final problem was solved inside of anotherFunction() upon further inspection.

Originally:

function anotherFunction(array) {
    return new Promise(function (resolve, reject) {
        return anotherGet(parcels).then(function (list) {
            /* Do stuff here without a return */
        });
    });
}

Fixed Version:

function anotherFunction(array) {
    return anotherGet(parcels).then(function (list) {
        /* Do stuff here */
        return list;
    });
}
Community
  • 1
  • 1
Hank
  • 475
  • 9
  • 28
  • https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it – SLaks Jan 02 '19 at 17:15
  • I don't think you need to wrap that in a Promise in the first place; you're already getting a Promise back from `getElse()`; just return that. Similarly the `.then()` handler can return the `getElseBox()` Promise. – Pointy Jan 02 '19 at 17:19
  • It's not clear in the question: is `anotherFunction()` asynchronous? – Mark Jan 02 '19 at 17:25
  • 1
    @MarkMeyer My apologies, but it is. I will add that to the post. – Hank Jan 02 '19 at 17:58
  • Updated question to hopefully better express issue. – Hank Jan 02 '19 at 18:14

2 Answers2

6

First, avoid the Promise/Deferred antipattern. Rarely you need to create your own promises, usually you have some function that returns a Promise; use that.

Second, for the outer Promise to wait for a nesed PRomise, you need to return that nested Promise to the outer Promise.
Returning a Promise inside of then() will make the resulting Promise to resolve to the value of the Promise you retuned inside.

And last, something like .then(value => value) or .then(value => Promise.resolve(someFunction(value)))is pointless. Your wrapper around anotherFunction basically just passed through the argument and returned the result.

So, your Pseudocode should look something like this:

function getSomething(text) {
  return getElse(text).then(function (items) {
    if (items.length !== 0) {
      /* Stuff here */
      return getElseElse(box).then(function (moreItems) {
        /* Stuff here */
        return array;
      }).then(anotherFunction);
    } else {
      return null;
    }
  });
}
Thomas
  • 11,958
  • 1
  • 14
  • 23
  • What I want to return from the whole thing is *result*, not *array*. The reason i passed in *array* and returned *result* is because *array* is an array of lookups and *result* is a class generated from the lookups with more information. I need to pass *array* into *anotherFunction()* so that I can return the result from that function and I need to wait until the before items have finished before I execute *anotherFunction()* using *array*. That is the reason why I did that part how I did (might still be wrong haha). How can I still return what I want using something like your sudo? – Hank Jan 02 '19 at 17:44
  • @Hank, as far as your snippet goes, `result = anotherFunction(array)`. That's exactly what `.then(anotherFunction);` does in my code. – Thomas Jan 02 '19 at 17:48
  • Ok that clears things up, then. I didn't know the syntax worked in that way. I will try it out and let you know if it works like I want it to, which I believe it will. I had a strong feeling my code was mainly just inefficient lol – Hank Jan 02 '19 at 17:50
  • I updated the post with more information that I hope will help. It seems to be closer to what I want it to be with your recommendations but *getSomething().then()* appears to not be waiting on the result correctly. – Hank Jan 02 '19 at 18:08
  • Figured out the final problem and updated the post alongside marking your answer. Thanks for the help – Hank Jan 02 '19 at 20:28
1

The basic trick is to make sure all of your resolve() calls are inside of the nested promises' then() methods.

Something along the lines of:

function getSomething(text) {
    return new Promise(function (resolve, reject) {
        getElse(text).then(function (items) {
            if (items.length !== 0) {

                /* Stuff here */

                getElseElse(box).then(function (moreItems) {

                    /* Stuff here */

                    return array;
                }.then(function (array) {
                    var promise = new Promise(function (resolve, reject) {
                        result = anotherFunction(array); <-- Spot 1
                    });
                    promise.then(function () { });

                    resolve(result); <--- Spot 2
                });    
            }
            else {
                resolve(null);
            }
        });
    });
};

If it needs to be nested in the promise.then() above, just stick it in there instead.

Also note that you don't need to return your resolves. Just call them.

samanime
  • 25,408
  • 15
  • 90
  • 139