下载大量数据时解决 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>