0

Please see this fiddle. I have a globalPromiseList array that controls a page loading spinner on my site. After all the promises in that array are done, remove the loading spinner. First declare global list before any JS:

var globalPromiseList = [];

Then I may have individual promises on the page for something I want done while spinner is hiding page. Here I create my individual promises and add them to the global promise list. NOTE: I must setup promise like this because I am hooking into events from third party libraries, I cannot put the code instead the containing promise block (see Resolve Javascript Promise outside function scope).

function someActionDone(id) {    
            window[id].resolve({ 
                then: function (result) {
                    console.log(id + " loaded!");
                    return result;
                }
            });
        };

        [
            ("groups-no-dropdown"),
            ("groups-default"),
            ("groups-extended"),
        ].forEach(function(id) {
            window[id] = { resolve: undefined, reject: undefined };

            window[id + "loaded"] = new Promise(function(resolve, reject) {
                window[id].resolve = resolve;
                window[id].reject = reject;
            });

            globalPromiseList.push(window[id + "loaded"]);
        });

        $(document).ready(function(){
            [("groups-no-dropdown"),
            ("groups-default"),
            ("groups-extended"),
            ].forEach(function(id) {
                  someActionDone(id);
            });
        });

Then all promise check to remove spinner:

Promise.all(globalPromiseList).then(function () {
            $("#loading-overlay").css("overflow", "unset");
            $("div#page.loadingoverlay").animate({
                    opacity: "toggle"
                },
                {
                    duration: 1200,
                    specialEasing: {
                        width: "linear"
                    },
                    done: function() {
                        $("div#page.loadingoverlay").toggle(false);
                    }
                });
            console.log('all promises finished');
        });      

However, all my promises stay in pending status, despite the console log being called in the .then. Please see this example: https://jsfiddle.net/DOTang/whwqp5nn/ Check console out, you see 3 loaded messages, but the promises are still pending.

I then try to call resolve().then() instead of supplying then inside the resolve call:

 window[id].resolve().then( 
            function (result) {
                console.log(id + " loaded!");
                return result;
            }
        );

But that gives me an error of:

Cannot read property 'then' of undefined

What am I doing wrong?

EDIT Thanks to the comments I was able to solve this like so: https://jsfiddle.net/DOTang/whwqp5nn/1/ my resolve/then usage was wrong. It should be like so, add:

window[id + "loaded"].then(function(id){
         console.log(id + " loaded!");
        });

Then change comboboxDataBound to:

function comboboxDataBound(id) {    
  window[id].resolve(id);
};
SventoryMang
  • 10,275
  • 15
  • 70
  • 113
  • I only glanced over your code, but do you think that you can add to the globalPromiseList after you already fired the `Promise.all()` and expect the it to acknowledge those pushes? If you do, that is the issue. – zero298 Oct 04 '17 at 21:05
  • I deleted my answer since it looks like that isn't the issue. – zero298 Oct 04 '17 at 21:15
  • I updated to code blocks so the order of my code is more clear. – SventoryMang Oct 04 '17 at 21:16
  • You're doing seriously weird stuff there. Try to avoid those global `resolve` deferreds. – Bergi Oct 04 '17 at 21:17
  • Resolving a promise with an object that has a `then` method is unwise - it is expected by the promise implementation that such an object acts like a promise and does call its callbacks. You won't get a `result` passed in there. – Bergi Oct 04 '17 at 21:18
  • 1
    Are you really resolving all those promises in `$(document).ready(…)`? Then I don't see any need to use promises here, just put the spinner-removing inside the `ready` callback. What is your [actual problem](https://meta.stackexchange.com/q/66377)? – Bergi Oct 04 '17 at 21:21
  • Well I don't even need it to return anything, I just added that in there to see if something had to be returned to fully resolve it. And you're looking at a simplified example. As I said. I am plugging into third party library events that have their own callbacks and function calls.The combobox databound function in the jsfiddle is an abstraction. I'm not literally just trying to complete a bunch of promises inside document.ready. Regardless, that doesn't explain why the promises stay pending. And I explained why I have to stick the resolves outside the promise. – SventoryMang Oct 04 '17 at 21:27
  • The actual problem is, as stated, my promises are staying as pending, despite the .then console logs executing, which would imply they resolved. – SventoryMang Oct 04 '17 at 21:27
  • 1
    `resolve()` is not designed to be chainable. `window[id].resolve(); window[id].then(...);` could be made to work but would only serve to mask some pretty serious design flaws. As Bergi says, what is your actual problem? – Roamer-1888 Oct 04 '17 at 21:32
  • 1
    @SventoryMang As I explained, you are *not* supposed to pass an object with a `then` method into `resolve`! No idea where you got that from. You should call `.then()` on a promise (one of the things in `globalPromiseList`). If you don't have any useful results, pass nothing to `resolve`; if those third party callbacks have arguments you might want to resolve with these values. – Bergi Oct 04 '17 at 21:32
  • @SventoryMang You might want to post your real code, as your simplification lacks a lot. At least the basic structure should be the same. – Bergi Oct 04 '17 at 21:35
  • @Roamer-1888 I've restated the problem twice now. I can't be anymore clear. @Bergi, the jsfiddle is fine, the structure is the same, and it reproduces my error exactly. But both your comments helped me solve it, not sure where I got that { then:} passed into resolve either, but it was somewhere online. Is it possible to resolve a promise as variable by calling something like `myPromiseVariable.resolve(). I see its possible statically but I can't use that. I'd like to get rid of the global resolves if possible. – SventoryMang Oct 04 '17 at 21:52
  • 1
    Usually you just wrap the `new Promise(resolve => { … })` around whatever uses the `resolve` callback. – Bergi Oct 04 '17 at 22:03
  • Dang can't do that for the reasons you mentioned (it wouldn't be possible to add to my global list) – SventoryMang Oct 04 '17 at 22:07
  • you can resolve to a thenable object, but the `then` method should behave like the `then` in every modern promise lib and take two functions, one that will be called with the value, and one that would be called with an error. Your `then`-method is buggy! If you change the line to `console.log(id + " loaded!", result)` you'll see that the argument called `result` contains a function. And if you call that function, you'll also get `all promises finished`. – Thomas Oct 04 '17 at 22:14
  • @SventoryMang, the need to externalise resolve/reject is rare. If I understand correctly, you are saying that "hooking into events from third party libraries" is the reason you believe you need to do so. You *may* be wrong in that assumption. It's possible you just need to promisify something(s). It's not possible to be certain without understanding the *whole* problem, not just your attempted solution. – Roamer-1888 Oct 04 '17 at 22:35
  • The library allows me to declare a method to be called after their event fires (such as widget is done loading), as supplied in the fiddle. As far as I know there's no way to both automatically have my promises be added to the global list and resolve them inside the function. I could just declare a promise inside the function and resolve it, but then I would have to manually check for it instead of doing the .all call. which is less than ideal. – SventoryMang Oct 05 '17 at 02:52
  • You appear to describe something that can be promisified. If so, then you should indeed be able to add a promise the global list and have it resolved from inside the promisified method. I know you have successfully debugged your own solution but feel that promisification needs a good airing here. Either way, invocation of the library are key. If you were to add that code to the question .... – Roamer-1888 Oct 05 '17 at 10:42

0 Answers0