SetTimeout vs. Ember.run.later in an ember app?

2019-03-12 00:59发布

问题:

Inside my handlebars template:

Today's date: {{currentDate}}
Current Time: {{currentTime}}

Inside my helpers:

Ember.Handlebars.registerBoundHelper 'currentDate', (option) ->
  moment().format('LL');

Ember.Handlebars.registerBoundHelper 'currentTime', (option) ->
  moment().format('h:mm:ss a');

How would I go about updating the currentTime to the view every 1 second?

I have read the Ember recommends Ember.run.later but I can't quite figure out where to put it and how to call it using this helper.

回答1:

You can use Ember.run.later like you would normally use setTimeout

Ember.run.later((function() {
  //do something in here that will run in 2 seconds
}), 2000);

I'm not sure of the internals, but I know that integration testing Ember requires that you use run.later (if you don't the test code won't wait for the timeout to finish).



回答2:

You won't want to add the timeout into the helper, you'll want to add it to a global variable and just do a diff from it. The reason you'll want to use Em.run.later is it will inject it into the run loop (part of what Toran was getting at). This is really important for testing.

Add the time to a global variable

App.ApplicationRoute = Em.Route.extend({
  setupController: function(controller, model){
    this._super(controller, model);
    this.startWatchingTime(controller);
  },
  startWatchingTime: function(controller){
    var self = this;
    controller.set('currentTime', moment());
    Em.run.later(function(){
      self.startWatchingTime(controller);
    }, 1000);
  }
});

Add it to the helper

Ember.Handlebars.helper('time-diff', function(date1, date2, format, options) {
  return date1.diff(date2, format);
});

send it into the helper

{{time-diff  controllers.application.currentTime anotherTime 'seconds'}}

http://emberjs.jsbin.com/UcUrIQa/1/edit



回答3:

you want to use Embers.run loop instead of going outside with setTimer.

The current (today) version of ember requires this format with context this (updating Toran Billups answer)

this._debouncedItem = Ember.run.later(this, () => {
   debugger;
}, 5000);

I highly recommend keeping a reference to the response of later() and cancelling it in the destroy hook

destroy() {
   this._super(...arguments);
   Ember.run.cancel(this._debouncedItem);
},


回答4:

You could make currentDate a regular property

currentDate: null,
currentTime: null

You can start this timer in the controller's constructor.

init: function () { 
  this.updateTimeProperty();
},
updateTimeProperty: function () {
  var _this = this;
  Ember.run.later(this, function() {
    _this.currentDate = moment().format('LL');
    _this.set('currentTime',  moment().format('h:mm:ss a');
    _this.updateTimeProperty());
  }, 1000);
}


回答5:

I'm bit outdated Ember user, but I would do it like this hope there is a better solution.

App.CurrentTimeView = Ember.View.extend({
    template : Ember.Handlebars.compile("<span>{{view.currentTime}}</span>"),
    currentTime : null,
    init : function(){
        var view = this;
        view.set('currentTime', moment().format('h:mm:ss a'));
        setInterval((function(view){
            return function(){view.set('currentTime', moment().format('h:mm:ss a'));};
        })(view),1000);
    }
});

and in the template

{{view "App.CurrentTimeView"}}

To answer your question, Javascript has a single threaded execution (unless you use webworkers) which means it will do things one by one in a serial manner. When you use setInterval it will en-queue your function every x milliseconds into this main execution queue. setInterval uses the passed time to do its queuing. Ember run loops will kind of calculate bindings and other heavy things in each run loop, so at the end of the loop we are sure that out changes are there ready. There are hooks like Em.run.next to make sure those codes when being run will have the complete changes done in last run loop. Similarly when you pass a time to Em.run.later it will as well run after that much time and also supports a parameter to set this inside the function. Mostly when dealing with some variables or Model/Controller data inside the function.

In your case setInterval looks ok (for me).

http://jsfiddle.net/NQKvy/604/