add ability to run requests in background

This commit is contained in:
Leo Horie 2014-04-29 23:14:09 -04:00
parent f4a248f0a5
commit 91a32af76c
13 changed files with 123 additions and 41 deletions

View file

@ -62,10 +62,11 @@
</div>
<div class="col(9,9,12)">
<h2 id="change-log">Change Log</h2>
<p><a href="/mithril/archive/v0.1.11">v0.1.11</a> - maintenance</p>
<p><a href="/mithril/archive/v0.1.11">v0.1.11</a> - enhancement</p>
<h3 id="news-">News:</h3>
<ul>
<li>Added <code>m.route()</code> overload to allow reading of current route <a href="https://github.com/lhorie/mithril.js/issues/61">#61</a></li>
<li>Added <code>background</code> option to <code>m.request</code> to allow requests that don&#39;t affect rendering <a href="https://github.com/lhorie/mithril.js/issues/62">#62</a></li>
</ul>
<hr>
<p><a href="/mithril/archive/v0.1.10">v0.1.10</a> - maintenance</p>

View file

@ -222,28 +222,28 @@ Mithril = m = new function app(window) {
m.endComputation()
}
m.redraw = function() {
for (var i = 0; i < roots.length; i++) {
m.render(roots[i], modules[i].view(controllers[i]))
}
lastRedraw = now
}
function redraw() {
now = window.performance && window.performance.now ? window.performance.now() : new window.Date().getTime()
if (now - lastRedraw > 16) m.redraw()
if (now - lastRedraw > 16) redraw()
else {
var cancel = window.cancelAnimationFrame || window.clearTimeout
var defer = window.requestAnimationFrame || window.setTimeout
cancel(lastRedrawId)
lastRedrawId = defer(m.redraw, 0)
lastRedrawId = defer(redraw, 0)
}
}
function redraw() {
for (var i = 0; i < roots.length; i++) {
m.render(roots[i], modules[i].view(controllers[i]))
}
lastRedraw = now
}
var pendingRequests = 0, computePostRedrawHook = null
m.startComputation = function() {pendingRequests++}
m.endComputation = function() {
pendingRequests = Math.max(pendingRequests - 1, 0)
if (pendingRequests == 0) {
redraw()
m.redraw()
if (computePostRedrawHook) {
computePostRedrawHook()
computePostRedrawHook = null
@ -437,7 +437,7 @@ Mithril = m = new function app(window) {
}
m.request = function(xhrOptions) {
m.startComputation()
if (xhrOptions.background !== true) m.startComputation()
var deferred = m.deferred()
var serialize = xhrOptions.serialize || JSON.stringify
var deserialize = xhrOptions.deserialize || JSON.parse
@ -455,7 +455,7 @@ Mithril = m = new function app(window) {
else if (xhrOptions.type) response = new xhrOptions.type(response)
deferred.promise(response)
deferred[e.type == "load" ? "resolve" : "reject"](response)
m.endComputation()
if (xhrOptions.background !== true) m.endComputation()
}
ajax(xhrOptions)
deferred.promise.then = propBinder(deferred.promise)
@ -924,10 +924,17 @@ function testMithril(mock) {
m.render(root, ["bar"])
return root.childNodes.length == 1
})
test(function() {
//https://github.com/lhorie/mithril.js/issues/56
var root = mock.document.createElement("div")
m.render(root, m("div", "foo"))
return root.childNodes.length == 1
})
//end m.render
//m.redraw
test(function() {
mock.performance.$elapse(50)
var controller
var root = mock.document.createElement("div")
m.module(root, {
@ -936,7 +943,29 @@ function testMithril(mock) {
})
controller.value = "foo"
m.redraw()
return root.childNodes[0].nodeValue === "foo"
var lengthBefore = root.childNodes.length
mock.performance.$elapse(50)
m.redraw()
mock.performance.$elapse(50)
return lengthBefore === 0 && root.childNodes[0].nodeValue === "foo"
})
test(function() {
mock.performance.$elapse(50)
var count = 0
var root = mock.document.createElement("div")
m.module(root, {
controller: function() {},
view: function(ctrl) {
count++
}
})
m.redraw()
m.redraw()
m.redraw()
mock.performance.$elapse(50)
m.redraw()
mock.performance.$elapse(50)
return count === 2
})
//m.route

View file

@ -222,28 +222,28 @@ Mithril = m = new function app(window) {
m.endComputation()
}
m.redraw = function() {
for (var i = 0; i < roots.length; i++) {
m.render(roots[i], modules[i].view(controllers[i]))
}
lastRedraw = now
}
function redraw() {
now = window.performance && window.performance.now ? window.performance.now() : new window.Date().getTime()
if (now - lastRedraw > 16) m.redraw()
if (now - lastRedraw > 16) redraw()
else {
var cancel = window.cancelAnimationFrame || window.clearTimeout
var defer = window.requestAnimationFrame || window.setTimeout
cancel(lastRedrawId)
lastRedrawId = defer(m.redraw, 0)
lastRedrawId = defer(redraw, 0)
}
}
function redraw() {
for (var i = 0; i < roots.length; i++) {
m.render(roots[i], modules[i].view(controllers[i]))
}
lastRedraw = now
}
var pendingRequests = 0, computePostRedrawHook = null
m.startComputation = function() {pendingRequests++}
m.endComputation = function() {
pendingRequests = Math.max(pendingRequests - 1, 0)
if (pendingRequests == 0) {
redraw()
m.redraw()
if (computePostRedrawHook) {
computePostRedrawHook()
computePostRedrawHook = null
@ -437,7 +437,7 @@ Mithril = m = new function app(window) {
}
m.request = function(xhrOptions) {
m.startComputation()
if (xhrOptions.background !== true) m.startComputation()
var deferred = m.deferred()
var serialize = xhrOptions.serialize || JSON.stringify
var deserialize = xhrOptions.deserialize || JSON.parse
@ -455,7 +455,7 @@ Mithril = m = new function app(window) {
else if (xhrOptions.type) response = new xhrOptions.type(response)
deferred.promise(response)
deferred[e.type == "load" ? "resolve" : "reject"](response)
m.endComputation()
if (xhrOptions.background !== true) m.endComputation()
}
ajax(xhrOptions)
deferred.promise.then = propBinder(deferred.promise)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -63,7 +63,7 @@
<div class="col(9,9,12)">
<h2 id="m-redraw">m.redraw</h2>
<p>Redraws the view for the currently active module. Use <a href="mithril.module"><code>m.module()</code></a> to activate a module.</p>
<p>This method is called internally by Mithril&#39;s auto-redrawing system and is only documented for completeness; you should avoid calling it manually unless you explicitly want a multi-pass redraw cycle.</p>
<p>This method is called internally by Mithril&#39;s auto-redrawing system and is only documented for completeness; usually you should avoid calling it manually unless you explicitly want a multi-pass redraw cycle. One case where <code>m.redraw</code> may be useful is to force a manual redraw after background requests (see the <code>background</code> option in <a href="mithril.request.html"><code>m.request</code></a>.</p>
<p>A multi-pass redraw cycle is usually only useful if you need non-trivial UI metrics measurements. A multi-pass cycle may span multiple browser repaints and therefore could cause flash of unbehaviored content (FOUC) and performance degradation.</p>
<p>By default, if you&#39;re using either <a href="mithril.route.html"><code>m.route</code></a> or <a href="mithril.module.html"><code>m.module</code></a>, <code>m.redraw()</code> is called automatically by Mithril&#39;s auto-redrawing system once the controller finishes executing.</p>
<p><code>m.redraw</code> is also called automatically on event handlers defined in virtual elements.</p>

View file

@ -255,6 +255,7 @@ where:
[String user,]
[String password,]
[Object&lt;any&gt; data,]
[Boolean background,]
[any unwrapSuccess(any data),]
[any unwrapError(any data),]
[String serialize(any dataToSerialize),]
@ -285,6 +286,13 @@ where:
<li><p><strong>Object<any> data</strong> (optional)</p>
<p>Data to be sent. It&#39;s automatically placed in the appropriate section of the request with the appropriate serialization based on <code>method</code></p>
</li>
<li><p><strong>Boolean background</strong> (optional)</p>
<p>Determines whether the <code>m.request</code> can affect template rendering. Defaults to false.</p>
<p>If this option is set to true, then the request does NOT call <a href="mithril.computation.html"><code>m.startComputation</code> / <code>m.endComputation</code></a>, and therefore the completion of the request does not trigger an update of the view, even if data has been changed. This option is useful for running operations in the background (i.e. without user intervention).</p>
<p>In order to force a redraw after a background request, use <a href="mithril.redraw.html"><code>m.redraw</code></a></p>
<pre><code class="lang-javascript">m.request({method: &quot;GET&quot;, url: &quot;/foo&quot;, background: true})
.then(m.redraw); //force redraw</code></pre>
</li>
<li><p><strong>any unwrapSuccess(any data)</strong> (optional)</p>
<p>A preprocessor function to unwrap the data from a success response in case the response contains metadata wrapping the data.</p>
<p>The default value (if this parameter is falsy) is the identity function <code>function(value) {return value}</code></p>

View file

@ -1,10 +1,11 @@
## Change Log
[v0.1.11](/mithril/archive/v0.1.11) - maintenance
[v0.1.11](/mithril/archive/v0.1.11) - enhancement
### News:
- Added `m.route()` overload to allow reading of current route [#61](https://github.com/lhorie/mithril.js/issues/61)
- Added `background` option to `m.request` to allow requests that don't affect rendering [#62](https://github.com/lhorie/mithril.js/issues/62)
---

View file

@ -2,7 +2,7 @@
Redraws the view for the currently active module. Use [`m.module()`](mithril.module) to activate a module.
This method is called internally by Mithril's auto-redrawing system and is only documented for completeness; you should avoid calling it manually unless you explicitly want a multi-pass redraw cycle.
This method is called internally by Mithril's auto-redrawing system and is only documented for completeness; usually you should avoid calling it manually unless you explicitly want a multi-pass redraw cycle. One case where `m.redraw` may be useful is to force a manual redraw after background requests (see the `background` option in [`m.request`](mithril.request.md).
A multi-pass redraw cycle is usually only useful if you need non-trivial UI metrics measurements. A multi-pass cycle may span multiple browser repaints and therefore could cause flash of unbehaviored content (FOUC) and performance degradation.

View file

@ -290,6 +290,7 @@ where:
[String user,]
[String password,]
[Object<any> data,]
[Boolean background,]
[any unwrapSuccess(any data),]
[any unwrapError(any data),]
[String serialize(any dataToSerialize),]
@ -328,6 +329,19 @@ where:
Data to be sent. It's automatically placed in the appropriate section of the request with the appropriate serialization based on `method`
- **Boolean background** (optional)
Determines whether the `m.request` can affect template rendering. Defaults to false.
If this option is set to true, then the request does NOT call [`m.startComputation` / `m.endComputation`](mithril.computation.md), and therefore the completion of the request does not trigger an update of the view, even if data has been changed. This option is useful for running operations in the background (i.e. without user intervention).
In order to force a redraw after a background request, use [`m.redraw`](mithril.redraw.md)
```javascript
m.request({method: "GET", url: "/foo", background: true})
.then(m.redraw); //force redraw
```
- **any unwrapSuccess(any data)** (optional)
A preprocessor function to unwrap the data from a success response in case the response contains metadata wrapping the data.

View file

@ -222,28 +222,28 @@ Mithril = m = new function app(window) {
m.endComputation()
}
m.redraw = function() {
for (var i = 0; i < roots.length; i++) {
m.render(roots[i], modules[i].view(controllers[i]))
}
lastRedraw = now
}
function redraw() {
now = window.performance && window.performance.now ? window.performance.now() : new window.Date().getTime()
if (now - lastRedraw > 16) m.redraw()
if (now - lastRedraw > 16) redraw()
else {
var cancel = window.cancelAnimationFrame || window.clearTimeout
var defer = window.requestAnimationFrame || window.setTimeout
cancel(lastRedrawId)
lastRedrawId = defer(m.redraw, 0)
lastRedrawId = defer(redraw, 0)
}
}
function redraw() {
for (var i = 0; i < roots.length; i++) {
m.render(roots[i], modules[i].view(controllers[i]))
}
lastRedraw = now
}
var pendingRequests = 0, computePostRedrawHook = null
m.startComputation = function() {pendingRequests++}
m.endComputation = function() {
pendingRequests = Math.max(pendingRequests - 1, 0)
if (pendingRequests == 0) {
redraw()
m.redraw()
if (computePostRedrawHook) {
computePostRedrawHook()
computePostRedrawHook = null
@ -437,7 +437,7 @@ Mithril = m = new function app(window) {
}
m.request = function(xhrOptions) {
m.startComputation()
if (xhrOptions.background !== true) m.startComputation()
var deferred = m.deferred()
var serialize = xhrOptions.serialize || JSON.stringify
var deserialize = xhrOptions.deserialize || JSON.parse
@ -455,7 +455,7 @@ Mithril = m = new function app(window) {
else if (xhrOptions.type) response = new xhrOptions.type(response)
deferred.promise(response)
deferred[e.type == "load" ? "resolve" : "reject"](response)
m.endComputation()
if (xhrOptions.background !== true) m.endComputation()
}
ajax(xhrOptions)
deferred.promise.then = propBinder(deferred.promise)

View file

@ -320,10 +320,17 @@ function testMithril(mock) {
m.render(root, ["bar"])
return root.childNodes.length == 1
})
test(function() {
//https://github.com/lhorie/mithril.js/issues/56
var root = mock.document.createElement("div")
m.render(root, m("div", "foo"))
return root.childNodes.length == 1
})
//end m.render
//m.redraw
test(function() {
mock.performance.$elapse(50)
var controller
var root = mock.document.createElement("div")
m.module(root, {
@ -332,7 +339,29 @@ function testMithril(mock) {
})
controller.value = "foo"
m.redraw()
return root.childNodes[0].nodeValue === "foo"
var lengthBefore = root.childNodes.length
mock.performance.$elapse(50)
m.redraw()
mock.performance.$elapse(50)
return lengthBefore === 0 && root.childNodes[0].nodeValue === "foo"
})
test(function() {
mock.performance.$elapse(50)
var count = 0
var root = mock.document.createElement("div")
m.module(root, {
controller: function() {},
view: function(ctrl) {
count++
}
})
m.redraw()
m.redraw()
m.redraw()
mock.performance.$elapse(50)
m.redraw()
mock.performance.$elapse(50)
return count === 2
})
//m.route