我不确定你想偏离你当前的代码多远,但我想宣传一些淘汰赛的附加功能:)
如果您为请求创建一个小的“中间”模型,您可以使用computed 值来自动跟踪正确排序的数据列表。
例如,如果您像这样定义new Request():
var Request = function(start, end) {
this.completed = ko.observable(false);
this.data = [];
getData(start, end, this.onLoad.bind(this));
};
Request.prototype.onLoad = function(data) {
this.data = data;
this.completed(true);
};
您可以更改 for 循环来创建那些“中间”模型。这将为0 到100、101 到201 等创建一个Request。这些模型中的每一个都按创建顺序存储在一个数组中。
function getDataRequests(start, end, chunkSize) {
var requests = [];
for (var i = start; i < end; i += chunkSize) {
requests.push(new Request(i, Math.min(i + chunkSize, end)));
}
return requests;
};
现在您可以创建一个有序数组,您可以通过将所有completed 请求合并在一起来计算另一个data 的有序数组:
var DataVM = function(start, end, chunkSize) {
// We keep track of a list of requests
var requests = ko.observableArray(
getDataRequests(start, end, chunkSize)
);
// Because requests have an observable completed prop,
// we can automatically keep track of a list of completed
// requests
var completedRequests = ko.pureComputed(() =>
requests().filter(r => r.completed()));
// Now, whenever a requests completes, we flatten the
// `data` parts for `completed` requests
this.data = ko.pureComputed(() => completedRequests()
.reduce((items, r) => items.concat(r.data), []));
};
因为您拥有requests 数组,所以您可以轻松计算 UI 属性。例如:firstLoaded 是一个计算值,它返回您的 first 请求的 completed 值。
这是一个完整的例子(ES2015):
var DataVM = function(start, end, chunkSize) {
// We keep track of a list of requests
var requests = ko.observableArray(
getDataRequests(start, end, chunkSize)
);
// Because requests have an observable completed prop,
// we can automatically keep track of a list of completed
// requests
var completedRequests = ko.pureComputed(() =>
requests().filter(r => r.completed()));
// Now, whenever a requests completes, we flatten the
// `data` parts for `completed` requests
this.data = ko.pureComputed(() => completedRequests()
.reduce((items, r) => items.concat(r.data), []));
// Shows progress
this.loadingMsg = ko.pureComputed(() => {
var completedCount = completedRequests().length,
allCount = requests().length;
return completedCount === allCount
? `Done loading ${end - start} items in ${allCount} steps`
: `Loading... (${completedCount}/${allCount})`;
});
// Check if the first (if any) request has completed loading
this.firstCompleted = ko.pureComputed(() =>
requests().length && requests()[0].completed());
};
var Request = function(start, end) {
this.completed = ko.observable(false);
this.data = [];
getData(start, end, this.onLoad.bind(this));
};
Request.prototype.onLoad = function(data) {
this.data = data;
this.completed(true);
};
var vm = new DataVM(0, 50, 5);
ko.applyBindings(vm);
// Mock async ajax stuff and data getters
function getDataRequests(start, end, chunkSize) {
var requests = [];
for (var i = start; i < end; i += chunkSize) {
requests.push(new Request(i, Math.min(i + chunkSize, end)));
}
return requests;
};
function getData(start, end, cb) {
setTimeout(function() {
cb(mockData(start, end));
}, Math.random() * 3000 + 500);
}
function mockData(from, to) {
return Array(to - from).fill(from).map(function(_, i) {
return from + i;
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="text: loadingMsg"></div>
<ul data-bind="foreach: data, visible: firstCompleted" style="border: 1px solid black;">
<li data-bind="text: $data"></li>
</ul>