Reputation: 2248
I'm not an expert in JS or jQuery by any means. I'm trying to write code for a chat app that does something like this:
Now the issue I'm having is this. After getting the conversation list from the API call, I iterate over the list and bind each item to the pane. .each function goes to the next iteration before the bind function is done executing it's code block. When the function that has the $.each iterator is done executing, I use ".done()' to populate the chat history of the first conversation item. But since the loop is finished before all conversations are binded properly, the chat list is populated and appears before I can see any conversations. Because of this, I can't bind any events to the conversation.
What I'm trying to achieve is to wait for the bind conversation to finish executing and then continue with the iteration.
My code is as below:
function (data) {
$.each(data, function (index, value) {
if (value.toUser == loggeduser) {
var isMe = false;
getUser(value.fromUserID, function (user) {
if (user.picturename == 'none') {
bindConversationItem(value.chatMessages[0].message, user.username, 'nopic.png', value.conversationID);
}
else {
bindConversationItem(value.chatMessages[0].message, user.username, user.picturename, value.conversationID);
}
});
}
else {
getUser(value.toUserID, function (user) {
var isMe = true;
if (user.picturename == 'none') {
bindConversationItem(value.chatMessages[0].message, user.username, 'nopic.png', value.conversationID);
}
else {
bindConversationItem(value.chatMessages[0].message, user.username, user.picturename, value.conversationID);
}
});
}
});
}).done(function (data) {
populateFirstConversation(data);
bindEvents();
});
Here's my bindConversationItem function
function bindConversationItem(message, username, userimage, index) {
var conv_item = '<li> <a href="#" conversation-index = '+index+' class="clearfix">'
+ '<img src="http://localhost:1995/contents/member/' + userimage + '" alt="" class="img-circle">'
+ '<div class="friend-name">'
+ '<strong>' + username + '</strong>'
+ '</div>'
+ '<div class="last-message text-muted">' + message + '</div>'
+ '<small class="time text-muted">Just now</small>'
+ '<small class="chat-alert label label-danger">1</small>'
+ '</a>'
+ '</li>'
$("ul.friend-list").append(conv_item);
}
I tried wrapping the code inside a function and using a callback function but I can't seem to get it to work. Any solution here is appreciated.
Edit
I have the function "getUser' that is async in the for each loop. Here's what it looks like:
function getUser(userid, callback) {
$.ajax({
url: 'http://localhost:1995//api/member/' + userid
}).done(callback)
}
Upvotes: 1
Views: 3215
Reputation: 1
You can use .queue()
, $.map()
, .promise()
to return results from asynchronous calls in sequential order, perform tasks when all functions in queueName
have completed
function processQueue (data) {
return $({}).queue("ajax", $.map(data, function(id) {
return function(next) {
return getUser(id, function(val) {
// do stuff when asynchronous function returns a value
return $("<div>", {html:"id:" + id + ", value:" + val * Math.PI})
.appendTo("body").after("<br>");
}, next) // pass `next` function
}
})).dequeue("ajax").promise("ajax")
}
function getUser(value, callback, next) {
// do asynchronous stuff, e.g., `$.ajax()`
return new $.Deferred(function(dfd) {
setTimeout(function() {
dfd.resolve(value * 10)
}, Math.floor(Math.random() * 1500))
})
// chain `.then()` which calls `callback`, and `.then()`
// which call next function in `"ajax"` queue
.then(callback).then(next)
}
var arr = [1, 2, 3, 4, 5];
var queue = processQueue(arr);
// do stuff when queue completes
queue.then(function(data) {
console.log("complete")
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
Upvotes: 1
Reputation: 8287
The best solution of course is to use promises, but as you have reported you do not know very well about the language, this solution does not impact much your original code:
Instead $.each loop, execute every iteration at a time, then call the next when the callback fires.
function (data) {
function done() {
populateFirstConversation(data);
bindEvents();
}
function getNextConversation(index) {
if (index == data.length)
return done();
var value = data[index];
if (value.toUser == ...) {
getUser(..., function () {
...
getNextConversation(index + 1);
});
} else {
getUser(..., function () {
...
getNextConversation(index + 1);
});
}
}
getNextConversation(0);
}
Upvotes: 1