0

How does stack order work in javascript when using anonymous functions? I would expect the following code to print: "first call second call third call", but instead it prints: "second call third call first call".

function findTweets(params, num)
{
     params = {
        q: params, 
        count: num ,
        language: 'en' 
     }
     T.get('search/tweets', params, function(err, data, response) {
         console.log("first call");
        });

     console.log("second call");
}
findTweets("a str",1)
console.log("third call");

Any help would be greatly appreciated. Thanks

Pyram
  • 57
  • 6

2 Answers2

1

It's not the anonymity but the asynchrony that's tripping you up. You execute findTweets; that executes T.get which will in turn eventually execute a callback, and logs 'second call'; then findTweets is done, so your script logs 'third call'; finally, the async function executes its callback, and logs 'first call'.

After you invoke an asynchronous function, you should not do anything that depends on the state "as if" the asynchronous function had completed, because (as you've discovered) it frequently won't have.

If you have a more complex situation, there are libraries which can help handling asynchronous execution such as async, or you could use Promises.

dmfay
  • 2,417
  • 1
  • 11
  • 22
1

dmfay is correct that the asynchronous T.get() function is tripping you up.

Check out MDN's write-up on the concurrency model

JavaScript runs from an event loop where messages are processed from a queue. Each message is associated with a function. When the stack has capacity, a message is pulled from the queue and processed.

Every message is processed to completion, and messages can be added to the queue by functions, including functions that will be processed asynchronously like your T.get() function. In this case, if there are no other messages, T.get() is processed right away, but if there are other messages such as console.log('third call'), T.get() has to wait to insert it's message into the the stack, and it has to wait for it's callbacks to complete before it can invoke console.log().

function findTweets(params, num)
{
     params = {
        q: params, 
        count: num ,
        language: 'en' 
     }
     T.get('search/tweets', params, function(err, data, response) {
         console.log("first call"); // 3. Ok, I'm done!
        });

     console.log("second call"); // 4. Finally!
}
findTweets("a str",1) //1. I can start now but can't finish until my callbacks do
console.log("third call"); // 2. Sweet! I can go right now.

How can you get these to execute in the order you expect? Try a promise and a callback.

In order to see this in action, I stubbed the twitterAPI function with an object that contains the get() method. The stubbed get() method spoofs a delayed response using setTimeout()

var twitterAPI = function () {
  this.get = function (method, params, callback) {
    setTimeout(function () {
      callback()
    }, 1000)
  }
}

var T = new twitterAPI()

function findTweets (params, num, callback) {
  params = {
    q: params,
    count: num,
    language: 'en'
  }

  var p1 = new Promise(function(resolve, reject) {
    T.get('search/tweets', params, function (err, data, response) {
      resolve("first call") // first call resolved as a promise
    })
  })

  p1.then(function(res) {
    console.log(res) // log resolved promise
    console.log("second call") // make second call
    return callback() // return callback so third call can go
  })
}

findTweets("a str", 1, function () {
  console.log("third call") // Finally, third call is called
})

/*
first call
second call
third call
*/
Joe Creager
  • 56
  • 1
  • 6
  • The explanation makes sense but, I am still getting them in the same order. – Pyram Dec 09 '16 at 23:48
  • Looks like you are right Pyram. I should have stubbed the function to validate my assumption the first time around. I edited my comment to include an accurate example with stubbed function to spoof the twitterAPI response. This example is mainly to show how you can control the flow using promises and callbacks. Let me know if this doesn't help. – Joe Creager Dec 10 '16 at 00:58