Merge pull request #1592 from pygy/async-redraw
Make m.redraw() purely asynchronous, add m.redraw.sync()
This commit is contained in:
commit
8ab31790ab
11 changed files with 381 additions and 116 deletions
|
|
@ -16,6 +16,6 @@ module.exports = function(redrawService) {
|
|||
redrawService.render(root, Vnode(component))
|
||||
}
|
||||
redrawService.subscribe(root, run)
|
||||
redrawService.redraw()
|
||||
run()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,26 +4,23 @@ var coreRenderer = require("../render/render")
|
|||
|
||||
function throttle(callback) {
|
||||
//60fps translates to 16.6ms, round it down since setTimeout requires int
|
||||
var time = 16
|
||||
var delay = 16
|
||||
var last = 0, pending = null
|
||||
var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout
|
||||
return function() {
|
||||
var now = Date.now()
|
||||
if (last === 0 || now - last >= time) {
|
||||
last = now
|
||||
callback()
|
||||
}
|
||||
else if (pending === null) {
|
||||
var elapsed = Date.now() - last
|
||||
if (pending === null) {
|
||||
pending = timeout(function() {
|
||||
pending = null
|
||||
callback()
|
||||
last = Date.now()
|
||||
}, time - (now - last))
|
||||
}, delay - elapsed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function($window) {
|
||||
|
||||
module.exports = function($window, throttleMock) {
|
||||
var renderService = coreRenderer($window)
|
||||
renderService.setEventCallback(function(e) {
|
||||
if (e.redraw === false) e.redraw = undefined
|
||||
|
|
@ -31,18 +28,24 @@ module.exports = function($window) {
|
|||
})
|
||||
|
||||
var callbacks = []
|
||||
var rendering = false
|
||||
|
||||
function subscribe(key, callback) {
|
||||
unsubscribe(key)
|
||||
callbacks.push(key, throttle(callback))
|
||||
callbacks.push(key, callback)
|
||||
}
|
||||
function unsubscribe(key) {
|
||||
var index = callbacks.indexOf(key)
|
||||
if (index > -1) callbacks.splice(index, 2)
|
||||
}
|
||||
function redraw() {
|
||||
for (var i = 1; i < callbacks.length; i += 2) {
|
||||
callbacks[i]()
|
||||
}
|
||||
function sync() {
|
||||
if (rendering) throw new Error("Nested m.redraw.sync() call")
|
||||
rendering = true
|
||||
for (var i = 1; i < callbacks.length; i+=2) try {callbacks[i]()} catch (e) {/*noop*/}
|
||||
rendering = false
|
||||
}
|
||||
|
||||
var redraw = (throttleMock || throttle)(sync)
|
||||
redraw.sync = sync
|
||||
return {subscribe: subscribe, unsubscribe: unsubscribe, redraw: redraw, render: renderService.render}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,14 @@ module.exports = function($window, redrawService) {
|
|||
var render, component, attrs, currentPath, lastUpdate
|
||||
var route = function(root, defaultRoute, routes) {
|
||||
if (root == null) throw new Error("Ensure the DOM element that was passed to `m.route` is not undefined")
|
||||
var run = function() {
|
||||
function run() {
|
||||
if (render != null) redrawService.render(root, render(Vnode(component, attrs.key, attrs)))
|
||||
}
|
||||
var redraw = function() {
|
||||
run()
|
||||
redraw = redrawService.redraw
|
||||
}
|
||||
redrawService.subscribe(root, run)
|
||||
var bail = function(path) {
|
||||
if (path !== defaultRoute) routeService.setPath(defaultRoute, null, {replace: true})
|
||||
else throw new Error("Could not resolve default route " + defaultRoute)
|
||||
|
|
@ -24,7 +29,7 @@ module.exports = function($window, redrawService) {
|
|||
component = comp != null && (typeof comp.view === "function" || typeof comp === "function")? comp : "div"
|
||||
attrs = params, currentPath = path, lastUpdate = null
|
||||
render = (routeResolver.render || identity).bind(routeResolver)
|
||||
run()
|
||||
redraw()
|
||||
}
|
||||
if (payload.view || typeof payload === "function") update({}, payload)
|
||||
else {
|
||||
|
|
@ -36,7 +41,6 @@ module.exports = function($window, redrawService) {
|
|||
else update(payload, "div")
|
||||
}
|
||||
}, bail)
|
||||
redrawService.subscribe(root, run)
|
||||
}
|
||||
route.set = function(path, data, options) {
|
||||
if (lastUpdate != null) {
|
||||
|
|
|
|||
|
|
@ -3,25 +3,29 @@
|
|||
var o = require("../../ospec/ospec")
|
||||
var components = require("../../test-utils/components")
|
||||
var domMock = require("../../test-utils/domMock")
|
||||
var throttleMocker = require("../../test-utils/throttleMock")
|
||||
|
||||
var m = require("../../render/hyperscript")
|
||||
var apiRedraw = require("../../api/redraw")
|
||||
var apiMounter = require("../../api/mount")
|
||||
|
||||
o.spec("mount", function() {
|
||||
var FRAME_BUDGET = Math.floor(1000 / 60)
|
||||
var $window, root, redrawService, mount, render
|
||||
var $window, root, redrawService, mount, render, throttleMock
|
||||
|
||||
o.beforeEach(function() {
|
||||
$window = domMock()
|
||||
throttleMock = throttleMocker()
|
||||
|
||||
root = $window.document.body
|
||||
|
||||
redrawService = apiRedraw($window)
|
||||
redrawService = apiRedraw($window, throttleMock.throttle)
|
||||
mount = apiMounter(redrawService)
|
||||
render = redrawService.render
|
||||
})
|
||||
|
||||
o.afterEach(function() {
|
||||
o(throttleMock.queueLength()).equals(0)
|
||||
})
|
||||
|
||||
o("throws on invalid component", function() {
|
||||
var threw = false
|
||||
try {
|
||||
|
|
@ -46,7 +50,7 @@ o.spec("mount", function() {
|
|||
o(threw).equals(true)
|
||||
})
|
||||
|
||||
o("renders into `root`", function() {
|
||||
o("renders into `root` synchronoulsy", function() {
|
||||
mount(root, createComponent({
|
||||
view : function() {
|
||||
return m("div")
|
||||
|
|
@ -68,7 +72,37 @@ o.spec("mount", function() {
|
|||
o(root.childNodes.length).equals(0)
|
||||
})
|
||||
|
||||
o("redraws on events", function(done) {
|
||||
o("Mounting a second root doesn't cause the first one to redraw", function() {
|
||||
var view = o.spy(function() {
|
||||
return m("div")
|
||||
})
|
||||
|
||||
render(root, [
|
||||
m("#child0"),
|
||||
m("#child1")
|
||||
])
|
||||
|
||||
mount(root.childNodes[0], createComponent({
|
||||
view : view
|
||||
}))
|
||||
|
||||
o(root.firstChild.nodeName).equals("DIV")
|
||||
o(view.callCount).equals(1)
|
||||
|
||||
mount(root.childNodes[1], createComponent({
|
||||
view : function() {
|
||||
return m("div")
|
||||
}
|
||||
}))
|
||||
|
||||
o(view.callCount).equals(1)
|
||||
|
||||
throttleMock.fire()
|
||||
|
||||
o(view.callCount).equals(1)
|
||||
})
|
||||
|
||||
o("redraws on events", function() {
|
||||
var onupdate = o.spy()
|
||||
var oninit = o.spy()
|
||||
var onclick = o.spy()
|
||||
|
|
@ -96,17 +130,12 @@ o.spec("mount", function() {
|
|||
o(onclick.args[0].type).equals("click")
|
||||
o(onclick.args[0].target).equals(root.firstChild)
|
||||
|
||||
// Wrapped to give time for the rate-limited redraw to fire
|
||||
setTimeout(function() {
|
||||
o(onupdate.callCount).equals(1)
|
||||
throttleMock.fire()
|
||||
|
||||
done()
|
||||
}, FRAME_BUDGET)
|
||||
o(onupdate.callCount).equals(1)
|
||||
})
|
||||
|
||||
o("redraws several mount points on events", function(done, timeout) {
|
||||
timeout(60)
|
||||
|
||||
o("redraws several mount points on events", function() {
|
||||
var onupdate0 = o.spy()
|
||||
var oninit0 = o.spy()
|
||||
var onclick0 = o.spy()
|
||||
|
|
@ -153,26 +182,26 @@ o.spec("mount", function() {
|
|||
o(onclick0.callCount).equals(1)
|
||||
o(onclick0.this).equals(root.childNodes[0].firstChild)
|
||||
|
||||
setTimeout(function() {
|
||||
o(onupdate0.callCount).equals(1)
|
||||
o(onupdate1.callCount).equals(1)
|
||||
throttleMock.fire()
|
||||
|
||||
root.childNodes[1].firstChild.dispatchEvent(e)
|
||||
o(onclick1.callCount).equals(1)
|
||||
o(onclick1.this).equals(root.childNodes[1].firstChild)
|
||||
o(onupdate0.callCount).equals(1)
|
||||
o(onupdate1.callCount).equals(1)
|
||||
|
||||
setTimeout(function() {
|
||||
o(onupdate0.callCount).equals(2)
|
||||
o(onupdate1.callCount).equals(2)
|
||||
root.childNodes[1].firstChild.dispatchEvent(e)
|
||||
|
||||
done()
|
||||
}, FRAME_BUDGET)
|
||||
}, FRAME_BUDGET)
|
||||
o(onclick1.callCount).equals(1)
|
||||
o(onclick1.this).equals(root.childNodes[1].firstChild)
|
||||
|
||||
throttleMock.fire()
|
||||
|
||||
o(onupdate0.callCount).equals(2)
|
||||
o(onupdate1.callCount).equals(2)
|
||||
})
|
||||
|
||||
o("event handlers can skip redraw", function(done) {
|
||||
var onupdate = o.spy()
|
||||
o("event handlers can skip redraw", function() {
|
||||
var onupdate = o.spy(function(){
|
||||
throw new Error("This shouldn't have been called")
|
||||
})
|
||||
var oninit = o.spy()
|
||||
var e = $window.document.createEvent("MouseEvents")
|
||||
|
||||
|
|
@ -194,15 +223,12 @@ o.spec("mount", function() {
|
|||
|
||||
o(oninit.callCount).equals(1)
|
||||
|
||||
// Wrapped to ensure no redraw fired
|
||||
setTimeout(function() {
|
||||
o(onupdate.callCount).equals(0)
|
||||
throttleMock.fire()
|
||||
|
||||
done()
|
||||
}, FRAME_BUDGET)
|
||||
o(onupdate.callCount).equals(0)
|
||||
})
|
||||
|
||||
o("redraws when the render function is run", function(done) {
|
||||
o("redraws when the render function is run", function() {
|
||||
var onupdate = o.spy()
|
||||
var oninit = o.spy()
|
||||
|
||||
|
|
@ -220,17 +246,12 @@ o.spec("mount", function() {
|
|||
|
||||
redrawService.redraw()
|
||||
|
||||
// Wrapped to give time for the rate-limited redraw to fire
|
||||
setTimeout(function() {
|
||||
o(onupdate.callCount).equals(1)
|
||||
throttleMock.fire()
|
||||
|
||||
done()
|
||||
}, FRAME_BUDGET)
|
||||
o(onupdate.callCount).equals(1)
|
||||
})
|
||||
|
||||
o("throttles", function(done, timeout) {
|
||||
timeout(200)
|
||||
|
||||
o("throttles", function() {
|
||||
var i = 0
|
||||
mount(root, createComponent({view: function() {i++}}))
|
||||
var before = i
|
||||
|
|
@ -242,12 +263,11 @@ o.spec("mount", function() {
|
|||
|
||||
var after = i
|
||||
|
||||
setTimeout(function(){
|
||||
o(before).equals(1) // mounts synchronously
|
||||
o(after).equals(1) // throttles rest
|
||||
o(i).equals(2)
|
||||
done()
|
||||
},40)
|
||||
throttleMock.fire()
|
||||
|
||||
o(before).equals(1) // mounts synchronously
|
||||
o(after).equals(1) // throttles rest
|
||||
o(i).equals(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
var o = require("../../ospec/ospec")
|
||||
var domMock = require("../../test-utils/domMock")
|
||||
var throttleMocker = require("../../test-utils/throttleMock")
|
||||
var apiRedraw = require("../../api/redraw")
|
||||
|
||||
o.spec("redrawService", function() {
|
||||
|
|
@ -17,25 +18,39 @@ o.spec("redrawService", function() {
|
|||
redrawService.redraw()
|
||||
})
|
||||
|
||||
o("honours throttleMock", function() {
|
||||
var throttleMock = throttleMocker()
|
||||
redrawService = apiRedraw(domMock(), throttleMock.throttle)
|
||||
var spy = o.spy()
|
||||
|
||||
redrawService.subscribe(root, spy)
|
||||
|
||||
o(spy.callCount).equals(0)
|
||||
|
||||
redrawService.redraw()
|
||||
|
||||
o(spy.callCount).equals(0)
|
||||
|
||||
throttleMock.fire()
|
||||
|
||||
o(spy.callCount).equals(1)
|
||||
})
|
||||
|
||||
o("should run a single renderer entry", function(done) {
|
||||
var spy = o.spy()
|
||||
|
||||
redrawService.subscribe(root, spy)
|
||||
|
||||
o(spy.callCount).equals(0)
|
||||
|
||||
redrawService.redraw()
|
||||
|
||||
o(spy.callCount).equals(1)
|
||||
|
||||
redrawService.redraw()
|
||||
redrawService.redraw()
|
||||
redrawService.redraw()
|
||||
|
||||
o(spy.callCount).equals(1)
|
||||
o(spy.callCount).equals(0)
|
||||
setTimeout(function() {
|
||||
o(spy.callCount).equals(2)
|
||||
|
||||
o(spy.callCount).equals(1)
|
||||
|
||||
done()
|
||||
}, 20)
|
||||
})
|
||||
|
|
@ -54,27 +69,29 @@ o.spec("redrawService", function() {
|
|||
|
||||
redrawService.redraw()
|
||||
|
||||
o(spy1.callCount).equals(1)
|
||||
o(spy2.callCount).equals(1)
|
||||
o(spy3.callCount).equals(1)
|
||||
o(spy1.callCount).equals(0)
|
||||
o(spy2.callCount).equals(0)
|
||||
o(spy3.callCount).equals(0)
|
||||
|
||||
redrawService.redraw()
|
||||
|
||||
o(spy1.callCount).equals(1)
|
||||
o(spy2.callCount).equals(1)
|
||||
o(spy3.callCount).equals(1)
|
||||
o(spy1.callCount).equals(0)
|
||||
o(spy2.callCount).equals(0)
|
||||
o(spy3.callCount).equals(0)
|
||||
|
||||
setTimeout(function() {
|
||||
o(spy1.callCount).equals(2)
|
||||
o(spy2.callCount).equals(2)
|
||||
o(spy3.callCount).equals(2)
|
||||
|
||||
o(spy1.callCount).equals(1)
|
||||
o(spy2.callCount).equals(1)
|
||||
o(spy3.callCount).equals(1)
|
||||
|
||||
done()
|
||||
}, 20)
|
||||
})
|
||||
|
||||
o("should stop running after unsubscribe", function() {
|
||||
var spy = o.spy()
|
||||
o("should stop running after unsubscribe", function(done) {
|
||||
var spy = o.spy(function() {
|
||||
throw new Error("This shouldn't have been called")
|
||||
})
|
||||
|
||||
redrawService.subscribe(root, spy)
|
||||
redrawService.unsubscribe(root, spy)
|
||||
|
|
@ -82,9 +99,33 @@ o.spec("redrawService", function() {
|
|||
redrawService.redraw()
|
||||
|
||||
o(spy.callCount).equals(0)
|
||||
setTimeout(function() {
|
||||
o(spy.callCount).equals(0)
|
||||
|
||||
done()
|
||||
}, 20)
|
||||
})
|
||||
|
||||
o("does nothing on invalid unsubscribe", function() {
|
||||
o("should stop running after unsubscribe, even if it occurs after redraw is requested", function(done) {
|
||||
var spy = o.spy(function() {
|
||||
throw new Error("This shouldn't have been called")
|
||||
})
|
||||
|
||||
redrawService.subscribe(root, spy)
|
||||
|
||||
redrawService.redraw()
|
||||
|
||||
redrawService.unsubscribe(root, spy)
|
||||
|
||||
o(spy.callCount).equals(0)
|
||||
setTimeout(function() {
|
||||
o(spy.callCount).equals(0)
|
||||
|
||||
done()
|
||||
}, 20)
|
||||
})
|
||||
|
||||
o("does nothing on invalid unsubscribe", function(done) {
|
||||
var spy = o.spy()
|
||||
|
||||
redrawService.subscribe(root, spy)
|
||||
|
|
@ -92,6 +133,39 @@ o.spec("redrawService", function() {
|
|||
|
||||
redrawService.redraw()
|
||||
|
||||
o(spy.callCount).equals(1)
|
||||
setTimeout(function() {
|
||||
o(spy.callCount).equals(1)
|
||||
|
||||
done()
|
||||
}, 20)
|
||||
})
|
||||
|
||||
o("redraw.sync() redraws all roots synchronously", function() {
|
||||
var el1 = $document.createElement("div")
|
||||
var el2 = $document.createElement("div")
|
||||
var el3 = $document.createElement("div")
|
||||
var spy1 = o.spy()
|
||||
var spy2 = o.spy()
|
||||
var spy3 = o.spy()
|
||||
|
||||
redrawService.subscribe(el1, spy1)
|
||||
redrawService.subscribe(el2, spy2)
|
||||
redrawService.subscribe(el3, spy3)
|
||||
|
||||
o(spy1.callCount).equals(0)
|
||||
o(spy2.callCount).equals(0)
|
||||
o(spy3.callCount).equals(0)
|
||||
|
||||
redrawService.redraw.sync()
|
||||
|
||||
o(spy1.callCount).equals(1)
|
||||
o(spy2.callCount).equals(1)
|
||||
o(spy3.callCount).equals(1)
|
||||
|
||||
redrawService.redraw.sync()
|
||||
|
||||
o(spy1.callCount).equals(2)
|
||||
o(spy2.callCount).equals(2)
|
||||
o(spy3.callCount).equals(2)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
var o = require("../../ospec/ospec")
|
||||
var callAsync = require("../../test-utils/callAsync")
|
||||
var browserMock = require("../../test-utils/browserMock")
|
||||
var throttleMocker = require("../../test-utils/throttleMock")
|
||||
|
||||
var m = require("../../render/hyperscript")
|
||||
var callAsync = require("../../test-utils/callAsync")
|
||||
|
|
@ -14,19 +15,23 @@ o.spec("route", function() {
|
|||
void [{protocol: "http:", hostname: "localhost"}, {protocol: "file:", hostname: "/"}].forEach(function(env) {
|
||||
void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) {
|
||||
o.spec("using prefix `" + prefix + "` starting on " + env.protocol + "//" + env.hostname, function() {
|
||||
var FRAME_BUDGET = Math.floor(1000 / 60)
|
||||
var $window, root, redrawService, route
|
||||
var $window, root, redrawService, route, throttleMock
|
||||
|
||||
o.beforeEach(function() {
|
||||
$window = browserMock(env)
|
||||
throttleMock = throttleMocker()
|
||||
|
||||
root = $window.document.body
|
||||
|
||||
redrawService = apiRedraw($window)
|
||||
redrawService = apiRedraw($window, throttleMock.throttle)
|
||||
route = apiRouter($window, redrawService)
|
||||
route.prefix(prefix)
|
||||
})
|
||||
|
||||
o.afterEach(function() {
|
||||
o(throttleMock.queueLength()).equals(0)
|
||||
})
|
||||
|
||||
o("throws on invalid `root` DOM node", function() {
|
||||
var threw = false
|
||||
try {
|
||||
|
|
@ -50,7 +55,7 @@ o.spec("route", function() {
|
|||
o(root.firstChild.nodeName).equals("DIV")
|
||||
})
|
||||
|
||||
o("routed mount points can redraw synchronously (POJO component)", function() {
|
||||
o("routed mount points only redraw asynchronously (POJO component)", function() {
|
||||
var view = o.spy()
|
||||
|
||||
$window.location.href = prefix + "/"
|
||||
|
|
@ -60,11 +65,14 @@ o.spec("route", function() {
|
|||
|
||||
redrawService.redraw()
|
||||
|
||||
o(view.callCount).equals(2)
|
||||
o(view.callCount).equals(1)
|
||||
|
||||
throttleMock.fire()
|
||||
|
||||
o(view.callCount).equals(2)
|
||||
})
|
||||
|
||||
o("routed mount points can redraw synchronously (constructible component)", function() {
|
||||
o("routed mount points only redraw asynchronously (constructible component)", function() {
|
||||
var view = o.spy()
|
||||
|
||||
var Cmp = function(){}
|
||||
|
|
@ -77,11 +85,14 @@ o.spec("route", function() {
|
|||
|
||||
redrawService.redraw()
|
||||
|
||||
o(view.callCount).equals(2)
|
||||
o(view.callCount).equals(1)
|
||||
|
||||
throttleMock.fire()
|
||||
|
||||
o(view.callCount).equals(2)
|
||||
})
|
||||
|
||||
o("routed mount points can redraw synchronously (closure component)", function() {
|
||||
o("routed mount points only redraw asynchronously (closure component)", function() {
|
||||
var view = o.spy()
|
||||
|
||||
function Cmp() {return {view: view}}
|
||||
|
|
@ -93,8 +104,11 @@ o.spec("route", function() {
|
|||
|
||||
redrawService.redraw()
|
||||
|
||||
o(view.callCount).equals(2)
|
||||
o(view.callCount).equals(1)
|
||||
|
||||
throttleMock.fire()
|
||||
|
||||
o(view.callCount).equals(2)
|
||||
})
|
||||
|
||||
o("default route doesn't break back button", function(done) {
|
||||
|
|
@ -160,11 +174,12 @@ o.spec("route", function() {
|
|||
o(oninit.callCount).equals(1)
|
||||
|
||||
redrawService.redraw()
|
||||
throttleMock.fire()
|
||||
|
||||
o(onupdate.callCount).equals(1)
|
||||
})
|
||||
|
||||
o("redraws on events", function(done) {
|
||||
o("redraws on events", function() {
|
||||
var onupdate = o.spy()
|
||||
var oninit = o.spy()
|
||||
var onclick = o.spy()
|
||||
|
|
@ -194,12 +209,9 @@ o.spec("route", function() {
|
|||
o(onclick.args[0].type).equals("click")
|
||||
o(onclick.args[0].target).equals(root.firstChild)
|
||||
|
||||
// Wrapped to give time for the rate-limited redraw to fire
|
||||
callAsync(function() {
|
||||
o(onupdate.callCount).equals(1)
|
||||
|
||||
done()
|
||||
})
|
||||
throttleMock.fire()
|
||||
o(onupdate.callCount).equals(1)
|
||||
})
|
||||
|
||||
o("event handlers can skip redraw", function(done) {
|
||||
|
|
@ -502,7 +514,10 @@ o.spec("route", function() {
|
|||
o(oninit.callCount).equals(1)
|
||||
route.set("/def")
|
||||
callAsync(function() {
|
||||
throttleMock.fire()
|
||||
|
||||
o(oninit.callCount).equals(2)
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
|
@ -538,23 +553,28 @@ o.spec("route", function() {
|
|||
route(root, "/a", {
|
||||
"/a" : {
|
||||
render: function() {
|
||||
return m("div")
|
||||
return m("div", m("p"))
|
||||
},
|
||||
},
|
||||
"/b" : {
|
||||
render: function() {
|
||||
return m("div")
|
||||
return m("div", m("a"))
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
var dom = root.firstChild
|
||||
var child = dom.firstChild
|
||||
|
||||
o(root.firstChild.nodeName).equals("DIV")
|
||||
|
||||
route.set("/b")
|
||||
|
||||
callAsync(function() {
|
||||
throttleMock.fire()
|
||||
|
||||
o(root.firstChild).equals(dom)
|
||||
o(root.firstChild.firstChild).notEquals(child)
|
||||
|
||||
done()
|
||||
})
|
||||
|
|
@ -588,6 +608,7 @@ o.spec("route", function() {
|
|||
o(renderCount).equals(1)
|
||||
|
||||
redrawService.redraw()
|
||||
throttleMock.fire()
|
||||
|
||||
o(matchCount).equals(1)
|
||||
o(renderCount).equals(2)
|
||||
|
|
@ -623,6 +644,7 @@ o.spec("route", function() {
|
|||
o(renderCount).equals(1)
|
||||
|
||||
redrawService.redraw()
|
||||
throttleMock.fire()
|
||||
|
||||
o(matchCount).equals(1)
|
||||
o(renderCount).equals(2)
|
||||
|
|
@ -818,10 +840,14 @@ o.spec("route", function() {
|
|||
})
|
||||
|
||||
callAsync(function() {
|
||||
throttleMock.fire()
|
||||
|
||||
route.set("/b")
|
||||
callAsync(function() {
|
||||
callAsync(function() {
|
||||
callAsync(function() {
|
||||
throttleMock.fire()
|
||||
|
||||
o(render.callCount).equals(0)
|
||||
o(component.view.callCount).equals(2)
|
||||
|
||||
|
|
@ -942,6 +968,7 @@ o.spec("route", function() {
|
|||
o(onmatch.callCount).equals(1)
|
||||
|
||||
redrawService.redraw()
|
||||
throttleMock.fire()
|
||||
|
||||
o(view.callCount).equals(2)
|
||||
o(onmatch.callCount).equals(1)
|
||||
|
|
@ -1020,6 +1047,8 @@ o.spec("route", function() {
|
|||
})
|
||||
|
||||
callAsync(function() {
|
||||
throttleMock.fire()
|
||||
|
||||
o(onmatch.callCount).equals(1)
|
||||
o(render.callCount).equals(1)
|
||||
|
||||
|
|
@ -1027,6 +1056,8 @@ o.spec("route", function() {
|
|||
|
||||
callAsync(function() {
|
||||
callAsync(function() {
|
||||
throttleMock.fire()
|
||||
|
||||
o(onmatch.callCount).equals(2)
|
||||
o(render.callCount).equals(2)
|
||||
|
||||
|
|
@ -1077,9 +1108,15 @@ o.spec("route", function() {
|
|||
route.set("/b")
|
||||
|
||||
callAsync(function() {
|
||||
throttleMock.fire()
|
||||
|
||||
o(root.firstChild.nodeName).equals("B")
|
||||
|
||||
route.set("/a")
|
||||
|
||||
callAsync(function() {
|
||||
throttleMock.fire()
|
||||
|
||||
o(root.firstChild.nodeName).equals("A")
|
||||
|
||||
done()
|
||||
|
|
@ -1144,7 +1181,9 @@ o.spec("route", function() {
|
|||
|
||||
route.set("/b")
|
||||
|
||||
// setting the route is asynchronous
|
||||
callAsync(function() {
|
||||
throttleMock.fire()
|
||||
o(spy.callCount).equals(1)
|
||||
|
||||
done()
|
||||
|
|
@ -1184,9 +1223,7 @@ o.spec("route", function() {
|
|||
})
|
||||
})
|
||||
|
||||
o("throttles", function(done, timeout) {
|
||||
timeout(200)
|
||||
|
||||
o("throttles", function() {
|
||||
var i = 0
|
||||
$window.location.href = prefix + "/"
|
||||
route(root, "/", {
|
||||
|
|
@ -1200,12 +1237,11 @@ o.spec("route", function() {
|
|||
redrawService.redraw()
|
||||
var after = i
|
||||
|
||||
setTimeout(function() {
|
||||
o(before).equals(1) // routes synchronously
|
||||
o(after).equals(2) // redraws synchronously
|
||||
o(i).equals(3) // throttles rest
|
||||
done()
|
||||
}, FRAME_BUDGET * 2)
|
||||
throttleMock.fire()
|
||||
|
||||
o(before).equals(1) // routes synchronously
|
||||
o(after).equals(1) // redraws asynchronously
|
||||
o(i).equals(2)
|
||||
})
|
||||
|
||||
o("m.route.param is available outside of route handlers", function(done) {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ You DON'T need to call it if data is modified within the execution context of an
|
|||
|
||||
You DO need to call it in `setTimeout`/`setInterval`/`requestAnimationFrame` callbacks, or callbacks from 3rd party libraries.
|
||||
|
||||
Typically, `m.redraw` triggers an asynchronous redraws, but it may trigger synchronously if Mithril detects it's possible to improve performance by doing so (i.e. if no redraw was requested within the last animation frame). You should write code assuming that it always redraws asynchronously.
|
||||
`m.redraw` always triggers an asynchronous redraws.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ Argument | Type | Required | D
|
|||
|
||||
##### m.route.set
|
||||
|
||||
Redirects to a matching route, or to the default route if no matching routes can be found.
|
||||
Redirects to a matching route, or to the default route if no matching routes can be found. Triggers an asynchronous redraw off all mount points.
|
||||
|
||||
`m.route.set(path, data, options)`
|
||||
|
||||
|
|
|
|||
91
test-utils/tests/test-throttleMock.js
Normal file
91
test-utils/tests/test-throttleMock.js
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
"use strict"
|
||||
|
||||
var o = require("../../ospec/ospec")
|
||||
var throttleMocker = require("../../test-utils/throttleMock")
|
||||
|
||||
o.spec("throttleMock", function() {
|
||||
o("works with one callback", function() {
|
||||
var throttleMock = throttleMocker()
|
||||
var spy = o.spy()
|
||||
|
||||
o(throttleMock.queueLength()).equals(0)
|
||||
|
||||
var throttled = throttleMock.throttle(spy)
|
||||
|
||||
o(throttleMock.queueLength()).equals(0)
|
||||
o(spy.callCount).equals(0)
|
||||
|
||||
throttled()
|
||||
|
||||
o(throttleMock.queueLength()).equals(1)
|
||||
o(spy.callCount).equals(0)
|
||||
|
||||
throttled()
|
||||
|
||||
o(throttleMock.queueLength()).equals(1)
|
||||
o(spy.callCount).equals(0)
|
||||
|
||||
throttleMock.fire()
|
||||
|
||||
o(throttleMock.queueLength()).equals(0)
|
||||
o(spy.callCount).equals(1)
|
||||
|
||||
throttleMock.fire()
|
||||
|
||||
o(spy.callCount).equals(1)
|
||||
})
|
||||
o("works with two callbacks", function() {
|
||||
var throttleMock = throttleMocker()
|
||||
var spy1 = o.spy()
|
||||
var spy2 = o.spy()
|
||||
|
||||
o(throttleMock.queueLength()).equals(0)
|
||||
|
||||
var throttled1 = throttleMock.throttle(spy1)
|
||||
|
||||
o(throttleMock.queueLength()).equals(0)
|
||||
o(spy1.callCount).equals(0)
|
||||
o(spy2.callCount).equals(0)
|
||||
|
||||
throttled1()
|
||||
|
||||
o(throttleMock.queueLength()).equals(1)
|
||||
o(spy1.callCount).equals(0)
|
||||
o(spy2.callCount).equals(0)
|
||||
|
||||
throttled1()
|
||||
|
||||
o(throttleMock.queueLength()).equals(1)
|
||||
o(spy1.callCount).equals(0)
|
||||
o(spy2.callCount).equals(0)
|
||||
|
||||
var throttled2 = throttleMock.throttle(spy2)
|
||||
|
||||
o(throttleMock.queueLength()).equals(1)
|
||||
o(spy1.callCount).equals(0)
|
||||
o(spy2.callCount).equals(0)
|
||||
|
||||
throttled2()
|
||||
|
||||
o(throttleMock.queueLength()).equals(2)
|
||||
o(spy1.callCount).equals(0)
|
||||
o(spy2.callCount).equals(0)
|
||||
|
||||
throttled2()
|
||||
|
||||
o(throttleMock.queueLength()).equals(2)
|
||||
o(spy1.callCount).equals(0)
|
||||
o(spy2.callCount).equals(0)
|
||||
|
||||
throttleMock.fire()
|
||||
|
||||
o(throttleMock.queueLength()).equals(0)
|
||||
o(spy1.callCount).equals(1)
|
||||
o(spy2.callCount).equals(1)
|
||||
|
||||
throttleMock.fire()
|
||||
|
||||
o(spy1.callCount).equals(1)
|
||||
o(spy2.callCount).equals(1)
|
||||
})
|
||||
})
|
||||
27
test-utils/throttleMock.js
Normal file
27
test-utils/throttleMock.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
"use strict"
|
||||
|
||||
module.exports = function() {
|
||||
var queue = []
|
||||
return {
|
||||
throttle: function(fn) {
|
||||
var pending = false
|
||||
return function() {
|
||||
if (!pending) {
|
||||
queue.push(function(){
|
||||
pending = false
|
||||
fn()
|
||||
})
|
||||
pending = true
|
||||
}
|
||||
}
|
||||
},
|
||||
fire: function() {
|
||||
var tasks = queue
|
||||
queue = []
|
||||
tasks.forEach(function(fn) {fn()})
|
||||
},
|
||||
queueLength: function(){
|
||||
return queue.length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -163,14 +163,24 @@ o.spec("api", function() {
|
|||
var count = 0
|
||||
var root = window.document.createElement("div")
|
||||
m.mount(root, createComponent({view: function() {count++}}))
|
||||
o(count).equals(1)
|
||||
m.redraw()
|
||||
o(count).equals(1)
|
||||
setTimeout(function() {
|
||||
m.redraw()
|
||||
|
||||
o(count).equals(2)
|
||||
|
||||
done()
|
||||
}, FRAME_BUDGET)
|
||||
})
|
||||
o("sync", function() {
|
||||
var root = window.document.createElement("div")
|
||||
var view = o.spy()
|
||||
m.mount(root, createComponent({view: view}))
|
||||
o(view.callCount).equals(1)
|
||||
m.redraw.sync()
|
||||
o(view.callCount).equals(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue