window.alert 模型回调后执行的 willTransition 回调中的代码

code in willTransition callback executed after model callback after a window.alert

我最近遇到了 Ember.js 的错误。基本上,当我在 Firefox 中单击浏览器后退按钮时,Ember.js 以错误的顺序执行代码。 alert("..."); 行后 willTransition 回调中的代码在 model 回调后执行,通常应在返回 alert("..."); 后立即执行。

代码是:

App = Ember.Application.create();
App.Router.reopen({
  location: 'history'
});
App.Router.map(function() {
  this.route('foo');
});
App.IndexRoute = Ember.Route.extend({
  actions:{
    willTransition: function(transition){
      console.log('start IndexRoute#willTransition');
      alert('See console logs');
      console.log('end IndexRoute#willTransition');
    }
  }
});
App.FooRoute = Ember.Route.extend({
  model: function() {
    console.log('start FooRoute#model');
    return [];
  }
});

可以在此处找到有关如何重现错误的说明:https://github.com/goooooouwa/location/blob/master/README.md

在 JSBin 上 使用 Firefox(OS X 上的版本 12+,Windows 上的版本 7+)查看此错误:http://emberjs.jsbin.com/tefoka/

此行为的根本原因是 this Firefox 错误。

此错误如何导致相关行为

引擎盖下发生了什么

  1. 用户点击浏览器后退按钮后,URL发生变化,浏览器历史记录发生变化,触发PopStateEvent,然后Ember用[=处理这个事件15=]回调
  2. 作为回调,Ember 通过调用 this._doURLTransition('handleURL', url);
  3. 开始转换
  4. 在转换内部,创建了一个 Promise 来确定此转换的分辨率。 Ember 通过调用 run.backburner.schedule('actions', function(){...}) 在 runloop 中调度 promise,因为没有创建 runloop,所以将通过调用 Backburner.createAutoRun():
  5. 创建一个 autorun
function createAutorun(backburner) {
  backburner.begin();
  backburner._autorun = global.setTimeout(function() {
    backburner._autorun = null; 
    backburner.end();
  });   
}

在自动创建的runloop结束之前,执行了以下代码:

willTransition: function(transition){
  console.log('-------------- 1. start IndexRoute#willTransition -------------- ');
  alert('See console logs');
  console.log('-------------- 2. end IndexRoute#willTransition -------------- ');
}

当runloop结束时,flush过程开始,执行如下代码:

model: function() {
  console.log('-------------- 3. start FooRoute#model -------------- ');
  return [];
}

简化版

以上代码与以下代码基本相同:

console.log('processing: task #1');
setTimeout(function(){
  console.log('processing: task #3');
},0);
alert('See console logs');
console.log('processing: task #2');

以上代码因 Firefox 错误而运行错误,这导致了有问题的 Ember 行为。