Gatonito
Gatonito

Reputation: 2174

Best way to return values from async functions in javascript

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?

Upvotes: 0

Views: 135

Answers (3)

Ritik Saxena
Ritik Saxena

Reputation: 724

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);

Upvotes: 1

T.J. Crowder
T.J. Crowder

Reputation: 1074305

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;
});

Upvotes: 1

Alex
Alex

Reputation: 38519

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...

Upvotes: 0

Related Questions