Seak Seak - 6 months ago 7
jQuery Question

function that returns a value from webservice

I know this is a highly answered topic, but reading tons of posts I can't figure if my dilema has a solution as I want.

I want to write a simple function that returns the User Name passing the UserId. It will be used everywhere to multiple purposes.

function getUserName(userId) {
//...some code to retrieve the Name from a REST webservice like $.ajax or $.getJSON or anything else that helps me achieve my need
return name;
}

$('#txtUserName').val(getUserName(sessionStorage.userId));
//...
if (getUserName(sessionStorage.userId) == getUserName($('#inputUser').val())) {
alert('Error: User typed with the same name as '+ sessionStorage.userID);
}


I know that can rewrite it all to put callback's or whatever, but I want to know if there's any implementation that makes possible to write this simple function that returns a value from a PHP webService.

I imagine a function like this:

function getUserName(userId) {
var users ={
'me': 'Seak Oink'
, 'you': 'Who knows'
, 'jd': 'John doe'
};
return users[userId];
}


...but instead of having the fixed users object, i retrieve it from my php webService that gets it from a DB.

Using a callback, makes impossible to handle values. For example (if I'd use callback and assuming calling
getUserName(userId, callback)
handles the function call):

$('#txtUserName').val('');
getUserName(sessionStorage.userId, function(userName) {
$('#txtUserName').val(userName);
});
if ($('#txtUserName').val() == '') {
alert('user '+ sessionStorage.userId +' doesn't exist');
}


Instead, you could answer me to put it into the callback, but if need to call again my function, I must nest it to a callback again and again... I think it's a bad programming practice:

$('#txtUserName').val('');
getUserName(sessionStorage.userId, function(userName) {
$('#txtUserName').val(userName);
getUserName($('#inputUser').val(), function (userName2) {
if (userName2 == userName) {
alert('Error: User typed with the same name as '+ sessionStorage.userID);
}
//....here I must continue the code flow instead of continuing to caller's flow.
//...nesting, and more nesting... impossible to read code?¿¿?:
userpermission(userId, function(bAllowed) {
if (bAllowed) {
saveRecord(userId, sKey, sSomeText, function () {
alert('record saved -or not-');
// ...and continue nesting
});
} else {
alert('forbidden');
}
});
});
});


... instead of this simple code flow logic:

var lOk = false;
$('#txtUserName').val('');
$('#txtUserName').val(getUserName(sessionStorage.userId);
if ($('#inputUser').val() == getUserName($('#inputUser').val())) {
alert('Error: User typed with the same name as '+ sessionStorage.userID);
}
if (userpermission(userId)) {
lOk = saveRecord(userId, sKey, sSomeText);
} else {
alert('forbidden');
}
if (lOk) {
alert('record saved');
}
// ...continue the validation process or whatever


I understand the simple example of retrieving a value with a callback, but don't using it in code logic.

I've been read How do I return the response from an asynchronous call? and much more like that and understood, but I can't uderstand how to use retrieved values from different sources and apply the necessary logic. Basicly, how to order the chaos?

Answer

Looks like you're experiencing callback hell. That happens when you have several asynchronous functions and you need to handle all their errors and success.

That's exactly for that case that promises have been invented.

If you don't have ES6, have a look on jquery promises, otherwise they are built-in : ES6 promise

They allow more readable, synchronous like code.

For example, you can do code like that:

 $.when( // waits for two promises to be resolved
        getUserName(userId)
            .then(function(name) {  // once name resolved, fetch profile (new promise)
                return getUserProfile(name); 
            }),
        getUserPoints(userId) // another independent promise
   ).done(function(userProfile, userPoints) { // the two above are done, I can move on and update the DOM
       $("#profile").doSomething(); 
       $("#points").doOtherThing();
   });