Skip to content

Commit

Permalink
Never ending stooorrreee.
Browse files Browse the repository at this point in the history
  • Loading branch information
eoinkelly committed Sep 15, 2014
1 parent 7dcea94 commit 12f9fe7
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 96 deletions.
65 changes: 9 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,68 +404,21 @@ Ember.Application.initializer({
});
```

### Does Ember wrap my async code in a runloop if I forget? If so how?
#### How is runloop behaviour different when testing?

```
STATUS: INCOMPLETE
the guide says that ember will wrap any ordinary async calls in a runloop - how?
I don't think this really happens!
The runloop guide says that ember will try to wrap async callbacks in a runloop
(http://emberjs.com/guides/understanding-ember/run-loop/#toc_what-happens-if-i-forget-to-start-a-run-loop-in-an-async-handler)
but I can't find where this happens within Ember code - can anyone give me any
pointers?
guide says:
* if ember detects an event handler running (how???) it opens a runloop and
closes it (which actually executes your code) on the next JS event loop turn
* this is bad because your code does not run in the turn you thought it would
and there can be a gap between turns if the browser decides to do GC etc.
```
Normally the runloop API functions

Aside: You really want your runlooop to start and end in a single JS frame
otherwise the browser might do otherwork if it spans frames e.g. GC


### How is Runloop behaviour different when testing?

```
STATUS: INCOMPLETE
* `run.schedule`
* `run.scheduleOnce`
* `run.once`

> "Some of Ember's test helpers are promises that wait for the run loop to empty before resolving."
will create a new runloop if one does not exist - these automatically (implicitly) created runloops are called _autoruns_. If `Ember.testing` is set then that behaviour is disabled. In fact when `Ember.testing` is set these three functions will throw an error if you run them at a time when there is not an existing runloop available.

Q: what ember funcs do defer() and deferOnce() map on to???
The reasons for this are:

Ember.backburner.defer() an Ember.backburner.deferOnce will create an
"autorun" runloop if no runloop is currently open. they call `checkAutoRun()` to
prevent that this behaviour when `Ember.testing` is set.
TODO: investigate this:
8. `Ember.Test` has its own internal `run()` that will use the normal runloop
`run()` if a runloop is open or otherwise just run the provide calback
synchronously
is this what the docs mean about disabling autoruns in testing ???
# auto run methods: createAutorun() and checkAutoRun()
* Ember.backburner.createAutorun()
* calls begin() immediately and schedules an end() (using setTimeout) on the very next turn of the event loop
* i.e. opens a runloop that will stay open for this turn of the event loop
* is only called by `backburner.defer()` and `backburner.deferOnce()`! iff there is not an already open runloop
Two places total!
* Ember.backburnder.checkAutoRun()
* If there isn't a currently open runloop, it will throw an error if `Ember.testing` is set.
1. _autoruns_ are Embers way of not punishing you in production if you forget to open a runloop before you schedule callbacks on it. While this is useful in production, these are still errors and are revealed as such in testing mode to help you find and fix them.
2. ???

checkAutoRun is called by 3 functions
Ember.run.
schedule()
scheduleOnce()
once()
```

### Summary

Expand Down
5 changes: 5 additions & 0 deletions experiments/autoruns/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ Ember.Application.initializer({
delete events.mousemove;
delete events.mouseenter;
delete events.mouseleave;

delete events.mousedown;
delete events.mouseup;
}
});

Expand Down Expand Up @@ -46,6 +49,8 @@ window.Todos = Ember.Application.create({
// }
});

Vuvuzela.setup(Todos);

Todos.Router.map(function() {
this.resource('todos', { path: '/' });
});
Expand Down
41 changes: 22 additions & 19 deletions experiments/autoruns/ember-1.7.0-noisy-runloop.js
Original file line number Diff line number Diff line change
Expand Up @@ -2452,6 +2452,7 @@ define("ember-application/system/application",
run.schedule('actions', self, '_initialize');
} else {
this.$().ready(function runInitialize() {
Ember.debug('Responding to DOMContentReady event');
run(self, '_initialize');
});
}
Expand Down Expand Up @@ -37129,6 +37130,7 @@ define("ember-views/system/event_dispatcher",
var self = this;

rootElement.on(event + '.ember', '.ember-view', function(evt, triggeringManager) {
Ember.debug('Handling event: ' + eventName);
var view = View.views[this.id],
result = true;

Expand Down Expand Up @@ -48234,6 +48236,7 @@ requireModule("ember");

})();


/**
* Vuvuzela
*
Expand Down Expand Up @@ -48275,28 +48278,28 @@ Vuvuzela = (function () {
var queues = Ember.run.backburner.currentInstance.queues;

var reports = names.map(function (name) {
return name + ": items:" + queues[name]._queue.length + "\n";
return name + ": items:" + queues[name]._queue.length + " (4 items per job)\n";
});
return "\n" + reports.join("* ");
return reports.join('* ');
}

// Vuvuzela = {};
// Vuvuzela.debug = Ember.debug;

//
// var setup = function (App) {
// var old_initialize = App._initialize;
//
// App._initialize = function () {
// Ember.debug('At start of _initialize() the queues look like:' + queuesReport());
// old_initialize.apply(App, arguments);
// Ember.debug('At end of _initialize() the queues look like:' + queuesReport());
// };
// };
//
return {
debug: Ember.debug
var setup = function (App) {
// var old_initialize = App._initialize;
// App._initialize = function () {
// Ember.debug('At start of _initialize() the queues look like:' + queuesReport());
// old_initialize.apply(App, arguments);
// Ember.debug('At end of _initialize() the queues look like:' + queuesReport());
// };
// var dispatcher = App.__container__.lookup('event_dispatcher:main');
// var oldHandler = dispatcher.setupHandler;
// dispatcher.setupHandler = function(rootElement, event, eventName) {
// // Ember.debug('Handling event: ' + eventName);
// oldHandler.apply(dispatcher, arguments);
// }
};

return {
debug: Ember.debug,
setup: setup
};
}());

8 changes: 4 additions & 4 deletions experiments/autoruns/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@
</head>
<body>

<div class="foo">
hi foo
</div>

<script type="text/x-handlebars" data-template-name="todos">
<div class="foo">
hi foo
</div>

Hi there
</script>
<!--
Expand Down
34 changes: 17 additions & 17 deletions experiments/noisy-runloop/ember-1.7.0-noisy-runloop.js
Original file line number Diff line number Diff line change
Expand Up @@ -48280,23 +48280,23 @@ Vuvuzela = (function () {
return reports.join('* ');
}

// Vuvuzela = {};
// Vuvuzela.debug = Ember.debug;

//
// var setup = function (App) {
// var old_initialize = App._initialize;
//
// App._initialize = function () {
// Ember.debug('At start of _initialize() the queues look like:' + queuesReport());
// old_initialize.apply(App, arguments);
// Ember.debug('At end of _initialize() the queues look like:' + queuesReport());
// };
// };
//
return {
debug: Ember.debug
var setup = function (App) {
// var old_initialize = App._initialize;
// App._initialize = function () {
// Ember.debug('At start of _initialize() the queues look like:' + queuesReport());
// old_initialize.apply(App, arguments);
// Ember.debug('At end of _initialize() the queues look like:' + queuesReport());
// };
var dispatcher = App.__container__.lookup('event_dispatcher:main');
var oldHandler = dispatcher.setupHandler;
dispatcher.setupHandler = function(rootElement, event, eventName) {
Ember.debug('Handling event: ' + eventName);
oldHandler.apply(dispatcher, arguments);
}
};

return {
debug: Ember.debug,
setup: setup
};
}());

45 changes: 45 additions & 0 deletions raw-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -828,3 +828,48 @@ debounce (target, method, args*, wait, immediate = false)
* "event storm" or at the end
```
### Does Ember wrap my async code in a runloop if I forget? If so how?
```
STATUS: INCOMPLETE

the guide says that ember will wrap any ordinary async calls in a runloop - how?
I don't think this really happens!

The runloop guide says that ember will try to wrap async callbacks in a runloop
(http://emberjs.com/guides/understanding-ember/run-loop/#toc_what-happens-if-i-forget-to-start-a-run-loop-in-an-async-handler)
but I can't find where this happens within Ember code - can anyone give me any
pointers?

guide says:
* if ember detects an event handler running (how???) it opens a runloop and
closes it (which actually executes your code) on the next JS event loop turn
* this is bad because your code does not run in the turn you thought it would
and there can be a gap between turns if the browser decides to do GC etc.
```
Aside: You really want your runlooop to start and end in a single JS frame
otherwise the browser might do otherwork if it spans frames e.g. GC
### How is Runloop behaviour different when testing?
## How autorunning works normally
* backburner.createAutorun()
* calls begin() immediately and schedules an end() (using setTimeout) on the very next turn of the event loop
i.e. opens a runloop that will stay open for this turn of the event loop
* does NOT pay attention to Ember.testing
* backburner.checkAutoRun
* If there isn't a currently open runloop, it will throw an error if `Ember.testing` is set.
* its purpose is to stop your program if Ember.testing is set and there isn't a runloop already open
* checkAutoRun is called by 3 functions
run.schedule()
run.scheduleOnce()
run.once()
* createAutorun is called by 2 functions
backburner.defer()
backburner.deferOnce()
65 changes: 65 additions & 0 deletions vuvuzela.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* Vuvuzela
*
* Make the Ember Runloop noisy on the console to help visualise its action.
*
* This patch will seriously degrade the performance of your Ember application
* so should never be applied to production code.
*
*/

Vuvuzela = (function () {
var bb = Ember.run.backburner,
oldBegin = bb.begin,
oldEnd = bb.end,
oldFlush = bb.flush,
runLoopId = 0;

Ember.run.backburner.begin = function () {
oldBegin.apply(bb, arguments);
bb.currentInstance.__uniqueId = ++runLoopId;
Ember.debug('Opening queues in Runloop: ' + bb.currentInstance.__uniqueId);
};

Ember.run.backburner.end = function () {
var currentId = bb.currentInstance.__uniqueId;
Ember.debug('Closing queues in Runloop ' + currentId + ' to outside code');
Ember.debug('The queues look like:' + queuesReport());
oldEnd.apply(bb, arguments);
};

Ember.run.backburner.flush = function () {
var currentId = bb.currentInstance.__uniqueId;
Ember.debug('Flushing queues in Runloop: ' + currentId);
oldFlush.apply(bb, arguments);
};

function queuesReport() {
var names = Ember.run.queues;
var queues = Ember.run.backburner.currentInstance.queues;

var reports = names.map(function (name) {
return name + ": items:" + queues[name]._queue.length + " (4 items per job)\n";
});
return reports.join('\n* ');
}

// var setup = function (App) {
// // var old_initialize = App._initialize;
// // App._initialize = function () {
// // Ember.debug('At start of _initialize() the queues look like:' + queuesReport());
// // old_initialize.apply(App, arguments);
// // Ember.debug('At end of _initialize() the queues look like:' + queuesReport());
// // };
// // var dispatcher = App.__container__.lookup('event_dispatcher:main');
// // var oldHandler = dispatcher.setupHandler;
// // dispatcher.setupHandler = function(rootElement, event, eventName) {
// // // Ember.debug('Handling event: ' + eventName);
// // oldHandler.apply(dispatcher, arguments);
// // }
// };
//
// return {
// debug: Ember.debug
// };
}());

0 comments on commit 12f9fe7

Please sign in to comment.