下载大量数据时解决 ember-data 中的锁定状态
Work-around the lock-state in ember-data when downloading large sets of data
我们一直在努力研究如何使用 Ember-data/Rest-adapter 优化下载大量数据的锁定状态。我们正在使用来自 REST API 的数据预加载一个应用程序,并且其中一组对某些用户来说具有 ~2M 的权重。我们要做的是避免应用程序在提取所有这些记录时遇到的锁定状态。
在此示例中,界面应该在每一帧上更新 i
,但是 "hangs" 一旦 JSON 下载并准备就绪。这当然与单线程执行有关,但必须有某种方式让它变得优雅?
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return [];
},
setupController: function(controller) {
var element = document.getElementById('counter');
var i = 0;
var l = function() {
element.innerHTML = i;
i++;
window.requestAnimationFrame(l);
}.bind(this);
l();
this.store.find('record').then(function(data){
console.log('loaded', data);
});
}
});
App.RecordModel = DS.Model.extend({
name: DS.attr('string'),
email: DS.attr('string'),
birthdate: DS.attr('date'),
created: DS.attr('date'),
});
App.RecordAdapter = DS.RESTAdapter.extend({
host: 'https://gist.githubusercontent.com/hussfelt/100fedf00009bdcbb962/raw/',
pathForType: function() {
return 'json_example.json';
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ember Starter Kit</title>
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://builds.emberjs.com/tags/v1.10.0/ember-template-compiler.js"></script>
<script src="http://builds.emberjs.com/tags/v1.10.0/ember.debug.js"></script>
<script src="http://builds.emberjs.com/beta/ember-data.min.js"></script>
<script src="app.js"></script>
</head>
<body>
<div id="counter"></div>
<script type="text/x-handlebars">
<h2>Welcome to Ember.js</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
</script>
</body>
</html>
解决方案是跳过使用 RESTAdapter 填充这组数据。
相反,我们会使用 Ember.$ 执行正常的 Ajax 请求,获取数据 - 然后以块的形式循环遍历数据并使用 store.pushPayload
注入存储.
感谢 freenode 的#emberjs 人员提供的想法!
肯定可以优化以下脚本,每次推送更多记录而不是一次推送一条记录。但它解决了问题,并最小化了锁定状态。
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return [];
},
setupController: function(controller) {
var element = document.getElementById('counter');
var i = 0;
var l = function() {
element.innerHTML = i;
i++;
window.requestAnimationFrame(l);
}.bind(this);
l();
// Prebuild options object
var options = {
// Requesting url
url: 'https://gist.githubusercontent.com/hussfelt/100fedf00009bdcbb962/raw/json_example.json',
// Using GET
type: 'GET',
// This is a cross-domain request
crossDomain: true,
// On successful request
success: function(data) {
// Run the inception-loop
recordLoop(Ember.$.parseJSON(data));
},
};
// Trigger the request
Ember.$.ajax(options);
// Disable the normal find for records
//this.store.find('record').then(function(data){
// console.log('loaded', data);
//});
/**
* Will populate the store in each 60th of a second
* @param object data The data to populate with
* @return void
*/
var recordLoop = function(data) {
// Setup counters
var x, i = 0;
// Prebuild awesome object - to match push-payload
var records = {
records: []
};
// Loop through records, populate array and push to store
for (x = (data.records.length - 1), i = 0;
(x >= 0 && i <= 300); x--, i++) {
// Prepare object
records.records = [data.records[x]];
// Push to store
this.store.pushPayload('record', records);
// Remove the actual element from the data
data.records.splice(x, 1);
}
// Run again, if we have content
if (data.records.length > 0) {
window.setTimeout(function() {
recordLoop(data);
}, 1000 / 60);
}
}.bind(this);
}
});
App.RecordModel = DS.Model.extend({
name: DS.attr('string'),
email: DS.attr('string'),
birthdate: DS.attr('date'),
created: DS.attr('date')
});
App.RecordAdapter = DS.RESTAdapter.extend({
host: 'https://gist.githubusercontent.com/hussfelt/100fedf00009bdcbb962/raw/',
pathForType: function() {
return 'json_example.json';
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ember Starter Kit</title>
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://builds.emberjs.com/tags/v1.10.0/ember-template-compiler.js"></script>
<script src="http://builds.emberjs.com/tags/v1.10.0/ember.debug.js"></script>
<script src="http://builds.emberjs.com/beta/ember-data.min.js"></script>
<script src="app.js"></script>
</head>
<body>
<div id="counter"></div>
<script type="text/x-handlebars">
<h2>Welcome to Ember.js</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
</script>
</body>
</html>
我们一直在努力研究如何使用 Ember-data/Rest-adapter 优化下载大量数据的锁定状态。我们正在使用来自 REST API 的数据预加载一个应用程序,并且其中一组对某些用户来说具有 ~2M 的权重。我们要做的是避免应用程序在提取所有这些记录时遇到的锁定状态。
在此示例中,界面应该在每一帧上更新 i
,但是 "hangs" 一旦 JSON 下载并准备就绪。这当然与单线程执行有关,但必须有某种方式让它变得优雅?
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return [];
},
setupController: function(controller) {
var element = document.getElementById('counter');
var i = 0;
var l = function() {
element.innerHTML = i;
i++;
window.requestAnimationFrame(l);
}.bind(this);
l();
this.store.find('record').then(function(data){
console.log('loaded', data);
});
}
});
App.RecordModel = DS.Model.extend({
name: DS.attr('string'),
email: DS.attr('string'),
birthdate: DS.attr('date'),
created: DS.attr('date'),
});
App.RecordAdapter = DS.RESTAdapter.extend({
host: 'https://gist.githubusercontent.com/hussfelt/100fedf00009bdcbb962/raw/',
pathForType: function() {
return 'json_example.json';
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ember Starter Kit</title>
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://builds.emberjs.com/tags/v1.10.0/ember-template-compiler.js"></script>
<script src="http://builds.emberjs.com/tags/v1.10.0/ember.debug.js"></script>
<script src="http://builds.emberjs.com/beta/ember-data.min.js"></script>
<script src="app.js"></script>
</head>
<body>
<div id="counter"></div>
<script type="text/x-handlebars">
<h2>Welcome to Ember.js</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
</script>
</body>
</html>
解决方案是跳过使用 RESTAdapter 填充这组数据。
相反,我们会使用 Ember.$ 执行正常的 Ajax 请求,获取数据 - 然后以块的形式循环遍历数据并使用 store.pushPayload
注入存储.
感谢 freenode 的#emberjs 人员提供的想法!
肯定可以优化以下脚本,每次推送更多记录而不是一次推送一条记录。但它解决了问题,并最小化了锁定状态。
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return [];
},
setupController: function(controller) {
var element = document.getElementById('counter');
var i = 0;
var l = function() {
element.innerHTML = i;
i++;
window.requestAnimationFrame(l);
}.bind(this);
l();
// Prebuild options object
var options = {
// Requesting url
url: 'https://gist.githubusercontent.com/hussfelt/100fedf00009bdcbb962/raw/json_example.json',
// Using GET
type: 'GET',
// This is a cross-domain request
crossDomain: true,
// On successful request
success: function(data) {
// Run the inception-loop
recordLoop(Ember.$.parseJSON(data));
},
};
// Trigger the request
Ember.$.ajax(options);
// Disable the normal find for records
//this.store.find('record').then(function(data){
// console.log('loaded', data);
//});
/**
* Will populate the store in each 60th of a second
* @param object data The data to populate with
* @return void
*/
var recordLoop = function(data) {
// Setup counters
var x, i = 0;
// Prebuild awesome object - to match push-payload
var records = {
records: []
};
// Loop through records, populate array and push to store
for (x = (data.records.length - 1), i = 0;
(x >= 0 && i <= 300); x--, i++) {
// Prepare object
records.records = [data.records[x]];
// Push to store
this.store.pushPayload('record', records);
// Remove the actual element from the data
data.records.splice(x, 1);
}
// Run again, if we have content
if (data.records.length > 0) {
window.setTimeout(function() {
recordLoop(data);
}, 1000 / 60);
}
}.bind(this);
}
});
App.RecordModel = DS.Model.extend({
name: DS.attr('string'),
email: DS.attr('string'),
birthdate: DS.attr('date'),
created: DS.attr('date')
});
App.RecordAdapter = DS.RESTAdapter.extend({
host: 'https://gist.githubusercontent.com/hussfelt/100fedf00009bdcbb962/raw/',
pathForType: function() {
return 'json_example.json';
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ember Starter Kit</title>
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://builds.emberjs.com/tags/v1.10.0/ember-template-compiler.js"></script>
<script src="http://builds.emberjs.com/tags/v1.10.0/ember.debug.js"></script>
<script src="http://builds.emberjs.com/beta/ember-data.min.js"></script>
<script src="app.js"></script>
</head>
<body>
<div id="counter"></div>
<script type="text/x-handlebars">
<h2>Welcome to Ember.js</h2>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
</script>
</body>
</html>