淘汰赛中的比赛条件
Race Conditions in Knockout
我最近 运行 在我的代码中发现了一个竞争条件的小问题。我正在使用 Knockout.Js 来收集一些要显示给用户的信息。
当需要在选择值之前生成下拉列表时出现问题。 通常下拉列表比其他请求小,将赢得比赛,因此不会造成问题。然而,我在我的应用程序中看到了一些情况,在较慢的互联网连接上,列表会在其次加载。下面是一个例子。如果列表中没有该值的选项,则无法选择它,并且对用户来说就好像没有被选中一样。
使用setTimeout
我模拟了这种体验。您可以交换这两个值以查看 "success" 场景。
function ViewModel() {
var self = this;
self.UserName = ko.observable();
self.UserGroup = ko.observable();
self.GroupList = ko.observableArray();
self.LoadUserGroups = function() {
//Ajax call to populate user groups
setTimeout(function() {
response = "Red Team,Blue Team,Green Team".split(",");
self.GroupList(response)
}, 2000) /// SWAP ME
}
self.LoadUserInformation = function() {
setTimeout(function() {
response = {
UserName: "John Pavek",
UserGroup: "Blue Team"
};
self.UserName(response.UserName);
self.UserGroup(response.UserGroup);
}, 1000) // SWAP ME
}
self.Load = function() {
self.LoadUserGroups();
self.LoadUserInformation();
}
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>
我可以在我的代码中添加什么,Vanilla 或 knockout 来防止这个问题的发生而不减慢整个体验?
您别无选择,只能等待 LoadUserInformation
应用信息,直到 在 从 LoadUserGroups
获取信息之后。但这根本不会减慢体验。
您需要将加载与显示信息分开。在非承诺环境中,我会让每个函数接受一个回调,然后只有在我得到两个结果后才继续(调用 "show" 函数),请参阅 ***
评论:
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>
当然,从技术上讲,如果群组在用户信息之前到达,没有什么可以阻止您显示这些群组,您只需使用状态标志即可:
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>
在 promise 环境中,我会有 "load" 函数 return promises,然后使用它们看起来像这样而不需要 checkDone
调用:
Promise.all([
self.LoadUserGroups().then(self.ShowUserGroups),
self.LoadUserInfo()
]).then(function(results) {
self.ShowUserInfo(results[1]);
});
...使用 ES2015+ 参数解构语法变得更清晰:
Promise.all([
self.LoadUserGroups().then(self.ShowUserGroups),
self.LoadUserInfo()
]).then(function([_, userInfo]) {
self.ShowUserInfo(userInfo);
});
当 LoadUserInformation
试图设置的值不在选项列表中时,select 正在覆盖 UserGroup
。但是您可以使用 valueAllowUnset
告诉它不要担心未知值。然后它不会覆盖。
function ViewModel() {
var self = this;
self.UserName = ko.observable();
self.UserGroup = ko.observable();
self.GroupList = ko.observableArray();
self.LoadUserGroups = function() {
//Ajax call to populate user groups
setTimeout(function() {
response = "Red Team,Blue Team,Green Team".split(",");
self.GroupList(response)
}, 2000) /// SWAP ME
}
self.LoadUserInformation = function() {
setTimeout(function() {
response = {
UserName: "John Pavek",
UserGroup: "Blue Team"
};
self.UserName(response.UserName);
self.UserGroup(response.UserGroup);
}, 1000) // SWAP ME
}
self.Load = function() {
self.LoadUserGroups();
self.LoadUserInformation();
}
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, valueAllowUnset: true"></select>
我最近 运行 在我的代码中发现了一个竞争条件的小问题。我正在使用 Knockout.Js 来收集一些要显示给用户的信息。
当需要在选择值之前生成下拉列表时出现问题。 通常下拉列表比其他请求小,将赢得比赛,因此不会造成问题。然而,我在我的应用程序中看到了一些情况,在较慢的互联网连接上,列表会在其次加载。下面是一个例子。如果列表中没有该值的选项,则无法选择它,并且对用户来说就好像没有被选中一样。
使用setTimeout
我模拟了这种体验。您可以交换这两个值以查看 "success" 场景。
function ViewModel() {
var self = this;
self.UserName = ko.observable();
self.UserGroup = ko.observable();
self.GroupList = ko.observableArray();
self.LoadUserGroups = function() {
//Ajax call to populate user groups
setTimeout(function() {
response = "Red Team,Blue Team,Green Team".split(",");
self.GroupList(response)
}, 2000) /// SWAP ME
}
self.LoadUserInformation = function() {
setTimeout(function() {
response = {
UserName: "John Pavek",
UserGroup: "Blue Team"
};
self.UserName(response.UserName);
self.UserGroup(response.UserGroup);
}, 1000) // SWAP ME
}
self.Load = function() {
self.LoadUserGroups();
self.LoadUserInformation();
}
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>
我可以在我的代码中添加什么,Vanilla 或 knockout 来防止这个问题的发生而不减慢整个体验?
您别无选择,只能等待 LoadUserInformation
应用信息,直到 在 从 LoadUserGroups
获取信息之后。但这根本不会减慢体验。
您需要将加载与显示信息分开。在非承诺环境中,我会让每个函数接受一个回调,然后只有在我得到两个结果后才继续(调用 "show" 函数),请参阅 ***
评论:
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>
当然,从技术上讲,如果群组在用户信息之前到达,没有什么可以阻止您显示这些群组,您只需使用状态标志即可:
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>
在 promise 环境中,我会有 "load" 函数 return promises,然后使用它们看起来像这样而不需要 checkDone
调用:
Promise.all([
self.LoadUserGroups().then(self.ShowUserGroups),
self.LoadUserInfo()
]).then(function(results) {
self.ShowUserInfo(results[1]);
});
...使用 ES2015+ 参数解构语法变得更清晰:
Promise.all([
self.LoadUserGroups().then(self.ShowUserGroups),
self.LoadUserInfo()
]).then(function([_, userInfo]) {
self.ShowUserInfo(userInfo);
});
当 LoadUserInformation
试图设置的值不在选项列表中时,select 正在覆盖 UserGroup
。但是您可以使用 valueAllowUnset
告诉它不要担心未知值。然后它不会覆盖。
function ViewModel() {
var self = this;
self.UserName = ko.observable();
self.UserGroup = ko.observable();
self.GroupList = ko.observableArray();
self.LoadUserGroups = function() {
//Ajax call to populate user groups
setTimeout(function() {
response = "Red Team,Blue Team,Green Team".split(",");
self.GroupList(response)
}, 2000) /// SWAP ME
}
self.LoadUserInformation = function() {
setTimeout(function() {
response = {
UserName: "John Pavek",
UserGroup: "Blue Team"
};
self.UserName(response.UserName);
self.UserGroup(response.UserGroup);
}, 1000) // SWAP ME
}
self.Load = function() {
self.LoadUserGroups();
self.LoadUserInformation();
}
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, valueAllowUnset: true"></select>