It is necessary to wait for the information from LoadUserGroups
before applying the data retrieved by LoadUserInformation
. However, this sequential process does not have to impede the user experience.
To streamline the process, it is essential to differentiate between the loading and displaying of the information. In a non-promise environment, each function would accept a callback, and the "show" functions would only be called after both results are obtained, as indicated by the comments in the code snippet below:
function ViewModel() {
var self = this;
self.UserName = ko.observable();
self.UserGroup = ko.observable();
self.GroupList = ko.observableArray();
// *** Only loads, doesn't show
self.LoadUserGroups = function(callback) {
//Ajax call to populate user groups
setTimeout(function() {
callback("Red Team,Blue Team,Green Team".split(","));
}, 2000);
}
// *** Shows
self.ShowUserGroups = function(groups) {
self.GroupList(groups);
};
// *** Only loads, doesn't show
self.LoadUserInformation = function(callback) {
setTimeout(function() {
callback({
UserName: "John Pavek",
UserGroup: "Blue Team"
});
}, 1000);
};
// *** Shows
self.ShowUserInformation = function(info) {
self.UserName(info.UserName);
self.UserGroup(info.UserGroup);
};
self.Load = function() {
var groups = null, userInfo = null;
self.LoadUserGroups(function(g) {
groups = g;
checkDone();
});
self.LoadUserInformation(function(u) {
userInfo = u;
checkDone();
});
function checkDone() {
if (groups && userInfo) {
// *** We have both, show them
self.ShowUserGroups(groups);
self.ShowUserInformation(userInfo);
}
}
}
self.Load();
}
ko.applyBindings(new ViewModel())
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
User Name:
<input data-bind="value: UserName" /> User Group:
<select data-bind="options: GroupList, optionsCaption: '--Pick a Team--', value: UserGroup"></select>
While waiting for the user info to arrive, you can still display the groups by using a state flag, as demonstrated in the following code snippet:
function ViewModel() {
var self = this;
self.UserName = ko.observable();
self.UserGroup = ko.observable();
self.GroupList = ko.observableArray();
// *** Only loads, doesn't show
self.LoadUserGroups = function(callback) {
//Ajax call to populate user groups
setTimeout(function() {
callback("Red Team,Blue Team,Green Team".split(","));
}, 2000);
}
// *** Shows
self.ShowUserGroups = function(groups) {
self.GroupList(groups);
};
// *** Only loads, doesn't show
self.LoadUserInformation = function(callback) {
setTimeout(function() {
callback({
UserName: "John Pavek",
UserGroup: "Blue Team"
});
}, 1000);
};
// *** Shows
self.ShowUserInformation = function(info) {
self.UserName(info.UserName);
self.UserGroup(info.UserGroup);
};
self.Load = function() {
var haveGroups = false, userInfo = null;
self.LoadUserGroups(function(groups) {
// *** No need to wait for the user info
self.ShowUserGroups(groups);
haveGroups = true;
checkDone();
});
self.LoadUserInformation(function(u) {
userInfo = u;
checkDone();
});
function checkDone() {
if (haveGroups && userInfo) {
// *** Show the user info
self.ShowUserInformation(userInfo);
}
}
}
self.Load();
}
ko.applyBindings(new ViewModel())
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
User Name:
<input data-bind="value: UserName" /> User Group:
<select data-bind="options: GroupList, optionsCaption: '--Pick a Team--', value: UserGroup"></select>
In a promise environment, utilizing promises within the "load" functions simplifies the process without the need for the checkDone
function. The updated implementation of handling promises is provided in the code snippets below:
Promise.all([
self.LoadUserGroups().then(self.ShowUserGroups),
self.LoadUserInfo()
]).then(function(results) {
self.ShowUserInfo(results[1]);
});
This concept can be further enhanced with ES2015+ parameter destructuring syntax, improving clarity and conciseness, as shown below:
Promise.all([
self.LoadUserGroups().then(self.ShowUserGroups),
self.LoadUserInfo()
]).then(function([_, userInfo]) {
self.ShowUserInfo(userInfo);
});