1

I am confused by this async behavior.

When token is false, refreshToken() function runs but the createTokenFile() doesn't wait for it to finish.

Shouldn't var tokenDate = new Date(token.expires); wait after callApiToken().then(function() {refreshToken();}) to finish before executing?

function createTokenFile() {
    console.log("No token-file.json file found. " .red +
            "Please complete for a new one." .red);
    return callApiToken().then(function() {
        refreshToken();
    });
}


function checkExpiredToken() {
    return new Promise(function(resolve, reject) {
        if (!token) {
            refreshToken();
        }
        var tokenDate = new Date(token.expires);
        var utc = new Date().toUTCString();
        var now  = new Date(utc);
 }


 function refreshToken() {
        try {
            var tokenFile = path.join(__dirname, 'token-file.json');
                console.log(tokenFile);
            return token = JSON.parse(fs.readFileSync(tokenFile, {encoding: 'utf-8'}));
        } catch (err) {
            if (err.code !== 'ENOENT') {
                throw err;
            } else {
                return createTokenFile();
            }
        }
    }  

UPDATED with refreshToken()

dman
  • 10,406
  • 18
  • 102
  • 201
  • 1
    What is the relationship between checkExpiredToken` and `createTokenFile`? – thefourtheye Mar 10 '15 at 01:25
  • 1
    No - that's why it's called "asynchronous". (It's a little hard to tell because it doesn't look like you posted all of your code; where's "refreshToken"? What calls "createTokenFile" and when/where?) – Pointy Mar 10 '15 at 01:25
  • 1
    `return aGlobalVariableThatINeverReference = theActualValueI wantedToReturn` seems silly. Why `return token = ....`? – Sukima Mar 10 '15 at 01:41
  • just trouble shooting... I was going to take it out. – dman Mar 10 '15 at 01:42

2 Answers2

2

Promise do not de-synchronize code. An function that is asynchronous will always be so. Therefore if refreshToken is asynchronous then it's use above will not wait for it to complete before moving on.

Your code sample leaves too much to the imagination (not to mention syntactically incorrect) so a better answer is not available. Perhaps try recreating the situation in jsbin.com or jsfiddle.net.

Sukima
  • 9,965
  • 3
  • 46
  • 60
  • 1
    `callApiToken` is asynchronous because it returns a promise. – Sukima Mar 10 '15 at 01:39
  • 1
    `checkExpiredToken` is asynchronous because it returns a promise. – Sukima Mar 10 '15 at 01:39
  • Is ` refreshToken` async just because it calls `createTokenFile` which has `callApiToken().then()`? Other than that I don't see what is making `refreshToken` async – dman Mar 10 '15 at 01:59
  • 1
    @dman: All functions execute synchronous, and return immediately. Some of the *start* asynchronous tasks, like your `refreshToken` did. If a function allows us to observe the result of this async operation, we call it `async`. This happens usually by either taking a callback argument or by returning a promise. – Bergi Mar 10 '15 at 02:31
  • When `callApiToken` is called it immediately returns a promise. Hence it is async. `refreshToken` in turn calls `callApiToken` which returns immediately. Yet your code assumes that `refreshToken` executes synchronously which isn't true. Check out this video: https://www.youtube.com/watch?v=8aGhZQkoFbQ that explains the event loop. – Sukima Mar 10 '15 at 10:49
2

Shouldn't var tokenDate = new Date(token.expires); wait after callApiToken().then(function() {refreshToken();}) to finish before executing?

No - it's not in a .then() callback that would wait for the promise to resolve. It only waits until the promise is created - but the promise resolution (that you call "finish") is asynchronous. Notice that promises are not magic, they're just callbacks.

To fix your code,

  • in createTokenFile you need to return the refreshToken() from the then callback
  • checkExpiredToken should not use the Promise constructor
  • refreshToken should always return a promise
  • there's no reason why refreshToken would read the file synchronously
  • you shouldn't cache the token as a global variable containing the value

function createTokenFile() {
    console.log("No token-file.json file found. " +
                "Please complete for a new one.");
    return callApiToken();
}

function checkExpiredToken() {
    return (tokenPromise || refreshToken())
    .then(function(token) {
        var tokenDate = new Date(token.expires);
        var utc = new Date().toUTCString();
        var now = new Date();
    });
}

function refreshToken() {
    var tokenFile = path.join(__dirname, 'token-file.json');
    console.log(tokenFile);
    return tokenPromise = readFileAsync(tokenFile, {encoding: 'utf-8'}))
    .then(JSON.parse)
    .catch(function(err) {
        if (err.code !== 'ENOENT') {
            throw err;
        } else {
            return createTokenFile().then(refreshToken);
        }
    });
}

(where readFileAsync is a promisified version of fs.readFile)

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Without using a `Promise constructor` can I send a reject on the catch? I tried reject() on the else block in catch but promise still acted as resolved. – dman Mar 10 '15 at 04:41
  • Why would you want to call `reject`? You can simply `throw` or `return` from a promise callback. – Bergi Mar 10 '15 at 04:42