0

I'm using the twit library for nodejs which have async calls, and I made functions like this:

function getUserFromSearch(phrase) {
    T.get('search/tweets', { q: phrase+' lang:pt', count: 1 }, function(err, data, response) {
    data['statuses'].forEach(function(element) {
        var result = "maybe";
        say('getting user profile from search for '+phrase, result);
        console.log(element);
        return element['user']['screen_name'];
    }, this);
    })
}

Since T.get() is executed asynchonously, whenever I do

var username = getUserFromSearch('banana');

I get username=undefined because it takes time to return things. The obvious solution would be to implement a callback like this:

function getUserFromSearch(phrase, callback) {
    T.get('search/tweets', { q: phrase+' lang:pt', count: 1 }, function(err, data, response) {
    data['statuses'].forEach(function(element) {
        var result = "maybe";
        say('getting user profile from search for '+phrase, result);
        console.log(element);
        callback(element['user']['screen_name']);
    }, this);
    })
}

But I don't think this is the best solution, because I need to create a callback function just for this. Isn't there a way to pass the username 'pointer' like this: getUserFromSearch('banana', username); in such a way that the function alters the value of username? Is there even a better way?

Gatonito
  • 1,662
  • 5
  • 26
  • 55

3 Answers3

1

Isn't there a way to pass the username 'pointer'

No, there isn't. JavaScript doesn't have references to variables.

You could pass in an object with a username property and have it set that property, but that tightly couples getUserFromSearch to a specific object and isn't composable.

Promises are a great way to handle this situation. They're composable and offer clear semantics around success and failure:

function getUserFromSearch(phrase) {
    return new Promise(function(resolve, reject) {
        T.get('search/tweets', { q: phrase + ' lang:pt', count: 1 }, function(err, data, response) {
            if (err) {
                reject(err);
            } else {
                data['statuses'].forEach(function(element) {
                    var result = "maybe";
                    say('getting user profile from search for ' + phrase, result);
                    resolve(element['user']['screen_name']);
                }, this);
            }
        })
    });
}

Usage:

getUserFromSearch(phrase).then(function(result) {
    username = result;
});

Yes, there's still a callback, but it's a callback that's readily composed with others.


With ES2015 syntax:

function getUserFromSearch(phrase) {
    return new Promise((resolve, reject) => {
        T.get('search/tweets', { q: phrase + ' lang:pt', count: 1 }, function(err, data, response) {
            if (err) {
                reject(err);
            } else {
                data['statuses'].forEach(element => {
                    const result = "maybe";
                    say('getting user profile from search for ' + phrase, result);
                    resolve(element['user']['screen_name']);
                });
            }
        })
    });
}

Usage:

getUserFromSearch(phrase).then(result => {
    username = result;
});
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Can I use ES2015 syntax in nodejs? – Gatonito Jan 07 '17 at 12:21
  • @Gatonito: Yes. Node v6.x has full support other than modules (and tail-call optimization unless you use a flag). v5.x and v4.x had partial support. – T.J. Crowder Jan 07 '17 at 12:21
  • 1
    Then you can use it like this: `var username = await getUserFromSearch(phrase);` once node supports [`async`/`await`](https://developers.google.com/web/fundamentals/getting-started/primers/async-functions). – jib Jan 07 '17 at 14:16
1

Since JavaScript doesn't have the notion of pointers,you can create an object before calling the function and pass the object as the second parameter to the function. Assign the result of the function to the object's property.

Your code would look like:

function getUserFromSearch(phrase,obj) {
T.get('search/tweets', { q: phrase+' lang:pt', count: 1 }, function(err, data, response) {
data['statuses'].forEach(function(element) {
    var result = "maybe";
    say('getting user profile from search for '+phrase, result);
    console.log(element);
    obj.username = element['user']['screen_name'];
}, this);
})
}

var obj = {username:null}
getUserFromSearch('banana',obj);
Ritik Saxena
  • 694
  • 1
  • 11
  • 23
0

This is the way javascript works.
Since this is an async function, you'll need to do it as you said.

getUserFromSearch('banana', function(username){
   //do something with username here
});

What you can do, is use something like async.js to reduce the nested callback hell...

Alex
  • 37,502
  • 51
  • 204
  • 332
  • What do you think of this idea: http://stackoverflow.com/questions/5292159/is-it-possible-to-change-the-value-of-the-function-parameter – Gatonito Jan 07 '17 at 12:19
  • Instead of passing a callback I could pass a object that contains the 'username' property and make the function modify it. – Gatonito Jan 07 '17 at 12:19
  • would love it if people commented, not just fly-by downvoted – Alex Jan 07 '17 at 12:43