1

I have this promise:

function getAPI(token)
{
return new Promise((resolve, reject) => {
    console.log("Request API");
    GM_xmlhttpRequest({
        method: "GET",
        url: "URL"+token,
        onload: function(response) {
            console.log(response.responseText);
            if( response.responseText == "NOT_ANSWER" || response.responseText.indexOf("ERRO") > -1 ){
                console.log(response.responseText + " - Calling Myself in 5 Seconds");
                setTimeout(function(){
                    getAPI(token);
                },5000);
            }
            else{
                console.log('Call API - Giving Result');
                resolve(response.responseText.split("_")[1]);
            }
        }
    });
});

}

I call it inside of itself when the answer is not what I want, cannot be less than 5 seconds though.

Then I do this in the main function:

setTimeout( function(){
                getAPI(token).then((key) => {
                    console.log(key);
                    doSomethingWithKey;
                    setTimeout( function(){
                        loop();
                    },1000);
                }).catch(() => {
                    console.log('Error na api - reload page!');
                    location.reload();
                });
            },25000);

But I noticed that when getAPI calls itself cause answer is not what i want, the '.then' in the main function never executes and my code hangs there. How can I fix it? I don't understand much of promises but I can't see why it hangs ...

Brock Adams
  • 90,639
  • 22
  • 233
  • 295

2 Answers2

1

You're creating multiple promises, because each call to getAPI creates and returns a new promise.

getAPI shouldn't call itself (or if it does, it should pass the new promise into resolve); instead, just retry the part within it you need to retry, something along these lines:

function getAPI(token) {
    return new Promise((resolve, reject) => {
        // Function to do the request
        function doRequest() {
            console.log("Request API");
            GM_xmlhttpRequest({
                method: "GET",
                url: "URL" + token,
                onload: function(response) {
                    console.log(response.responseText);
                    if (response.responseText == "NOT_ANSWER" || response.responseText.indexOf("ERRO") > -1) {
                        // Not what we wanted, retry
                        console.log(response.responseText + " - Calling Myself in 5 Seconds");
                        setTimeout(doRequest, 5000);
                    }
                    else {
                        console.log('Call API - Giving Result');
                        resolve(response.responseText.split("_")[1]);
                    }
                }
            });
        }
        doRequest();
    });
}

Side note: Your code using getAPI is checking for promise rejection, but nothing in getAPI ever rejects the promise.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • I will try. Indeed you are right i still need to figure what to catch there cause when i get what i DONT want i want to call it again, i will just remove the catch tbh. I see now what you mean, i needed to resolve the new getAPI call so when i get the correct answer it will recursively send the resolve to every promise created "backwards" and finaly give me the result i want. right ? – Pedro Oliveira Oct 29 '17 at 16:10
  • Btw, it's 'setTimeout(doRequest, 5000);' or 'setTimeout( function(){ doRequest }, 5000);' ? when i use the first case sometimes it get strange behaviours... – Pedro Oliveira Oct 29 '17 at 16:15
  • worked. Thank you very much _O_ i wasnt figuring it out – Pedro Oliveira Oct 29 '17 at 19:52
  • @PedroOliveira: The issues you've had with `setTimeout(doRequest, ...)` vs. `setTimeout(function() { doRequest(); }, ...)` probably relate to `this` within the call (which isn't relevant to `doReques`) or arguments passed to the function. E.g., there's a big difference between `setTimeout(obj.method, 5000);` and `setTimeout(function() { obj.method(); }, 5000);`, more [in this question's answers](http://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-context-inside-a-callback). – T.J. Crowder Oct 30 '17 at 08:34
1

I call it inside of itself when the answer is not what i want,

and then you don't call resolve of the promise which you had returned from the top getAPI call, so the promise never settles and your then callback never gets any result.

You should promisify your asynchronous functions GM_xmlhttpRequest and setTimeout on the lowest level, and then only chain your promises. By returning the result of the recursive call from a then callback, the resulting promise will resolve with the same result:

function xhrAsync(url) {
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            method: "GET",
            url: url,
            onload: resolve
        });
    });
}
function delayAsync(time) {
    return new Promise(resolve => {
        setTimeout(resolve, time);
    });
}
function getAPI(token) {
    console.log("Request API");
    return xhrAsync("URL"+token).then(response => {
//  ^^^^^^                       ^^^^
        console.log(response.responseText);
        if (response.responseText == "NOT_ANSWER" || response.responseText.includes("ERRO")) {
            console.log(response.responseText + " - Calling Myself in 5 Seconds");
            return delayAsync(5000).then(() => {
//          ^^^^^^                  ^^^^
                return getAPI(token);
//              ^^^^^^
            });
        } else {
            console.log('Call API - Giving Result');
            return response.responseText.split("_")[1];
        }
    });
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I assume your answer you would work too, although the other one was easier for me to implement. – Pedro Oliveira Oct 29 '17 at 19:56
  • Yes, it's a bit more to change but a cleaner and safer solution. By using promises everywhere, you get their benefits such as error recovery etc. If you like it, you can still upvote it while accepting the other answer :-) – Bergi Oct 29 '17 at 21:10
  • I actually had to use your soluction and it worked better. One question though. Lets say i have a timeout set before doing this calls and suddenly i go that timeout and i want to cancel all timeouts and calls being done here. Calling the "first" timeout in my function where getAPI is called will also cancel all timeouts inside or no? – Pedro Oliveira Nov 02 '17 at 21:52