Make m.redraw() strictly asynchronous
This commit is contained in:
parent
38956e119b
commit
b004c20f0c
11 changed files with 281 additions and 111 deletions
|
|
@ -16,6 +16,6 @@ module.exports = function(redrawService) {
|
||||||
redrawService.render(root, Vnode(component))
|
redrawService.render(root, Vnode(component))
|
||||||
}
|
}
|
||||||
redrawService.subscribe(root, run)
|
redrawService.subscribe(root, run)
|
||||||
redrawService.redraw()
|
run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,26 +4,23 @@ var coreRenderer = require("../render/render")
|
||||||
|
|
||||||
function throttle(callback) {
|
function throttle(callback) {
|
||||||
//60fps translates to 16.6ms, round it down since setTimeout requires int
|
//60fps translates to 16.6ms, round it down since setTimeout requires int
|
||||||
var time = 16
|
var delay = 16
|
||||||
var last = 0, pending = null
|
var last = 0, pending = null
|
||||||
var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout
|
var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout
|
||||||
return function() {
|
return function() {
|
||||||
var now = Date.now()
|
var elapsed = Date.now() - last
|
||||||
if (last === 0 || now - last >= time) {
|
if (pending === null) {
|
||||||
last = now
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
else if (pending === null) {
|
|
||||||
pending = timeout(function() {
|
pending = timeout(function() {
|
||||||
pending = null
|
pending = null
|
||||||
callback()
|
callback()
|
||||||
last = Date.now()
|
last = Date.now()
|
||||||
}, time - (now - last))
|
}, delay - elapsed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function($window) {
|
module.exports = function($window, throttleMock) {
|
||||||
|
var _throttle = throttleMock || throttle
|
||||||
var renderService = coreRenderer($window)
|
var renderService = coreRenderer($window)
|
||||||
renderService.setEventCallback(function(e) {
|
renderService.setEventCallback(function(e) {
|
||||||
if (e.redraw !== false) redraw()
|
if (e.redraw !== false) redraw()
|
||||||
|
|
@ -32,7 +29,7 @@ module.exports = function($window) {
|
||||||
var callbacks = []
|
var callbacks = []
|
||||||
function subscribe(key, callback) {
|
function subscribe(key, callback) {
|
||||||
unsubscribe(key)
|
unsubscribe(key)
|
||||||
callbacks.push(key, throttle(callback))
|
callbacks.push(key, _throttle(callback))
|
||||||
}
|
}
|
||||||
function unsubscribe(key) {
|
function unsubscribe(key) {
|
||||||
var index = callbacks.indexOf(key)
|
var index = callbacks.indexOf(key)
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,14 @@ module.exports = function($window, redrawService) {
|
||||||
var render, component, attrs, currentPath, lastUpdate
|
var render, component, attrs, currentPath, lastUpdate
|
||||||
var route = function(root, defaultRoute, routes) {
|
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")
|
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)))
|
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) {
|
var bail = function(path) {
|
||||||
if (path !== defaultRoute) routeService.setPath(defaultRoute, null, {replace: true})
|
if (path !== defaultRoute) routeService.setPath(defaultRoute, null, {replace: true})
|
||||||
else throw new Error("Could not resolve default route " + defaultRoute)
|
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"
|
component = comp != null && (typeof comp.view === "function" || typeof comp === "function")? comp : "div"
|
||||||
attrs = params, currentPath = path, lastUpdate = null
|
attrs = params, currentPath = path, lastUpdate = null
|
||||||
render = (routeResolver.render || identity).bind(routeResolver)
|
render = (routeResolver.render || identity).bind(routeResolver)
|
||||||
run()
|
redraw()
|
||||||
}
|
}
|
||||||
if (payload.view || typeof payload === "function") update({}, payload)
|
if (payload.view || typeof payload === "function") update({}, payload)
|
||||||
else {
|
else {
|
||||||
|
|
@ -36,7 +41,6 @@ module.exports = function($window, redrawService) {
|
||||||
else update(payload, "div")
|
else update(payload, "div")
|
||||||
}
|
}
|
||||||
}, bail)
|
}, bail)
|
||||||
redrawService.subscribe(root, run)
|
|
||||||
}
|
}
|
||||||
route.set = function(path, data, options) {
|
route.set = function(path, data, options) {
|
||||||
if (lastUpdate != null) options = {replace: true}
|
if (lastUpdate != null) options = {replace: true}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
var o = require("../../ospec/ospec")
|
var o = require("../../ospec/ospec")
|
||||||
var components = require("../../test-utils/components")
|
var components = require("../../test-utils/components")
|
||||||
var domMock = require("../../test-utils/domMock")
|
var domMock = require("../../test-utils/domMock")
|
||||||
|
var throttleMocker = require("../../test-utils/throttleMock")
|
||||||
|
|
||||||
var m = require("../../render/hyperscript")
|
var m = require("../../render/hyperscript")
|
||||||
var coreRenderer = require("../../render/render")
|
var coreRenderer = require("../../render/render")
|
||||||
|
|
@ -11,18 +12,22 @@ var apiMounter = require("../../api/mount")
|
||||||
|
|
||||||
o.spec("mount", function() {
|
o.spec("mount", function() {
|
||||||
var FRAME_BUDGET = Math.floor(1000 / 60)
|
var FRAME_BUDGET = Math.floor(1000 / 60)
|
||||||
var $window, root, redrawService, mount, render
|
var $window, root, redrawService, mount, render, throttleMock
|
||||||
|
|
||||||
o.beforeEach(function() {
|
o.beforeEach(function() {
|
||||||
$window = domMock()
|
$window = domMock()
|
||||||
|
throttleMock = throttleMocker()
|
||||||
|
|
||||||
root = $window.document.body
|
root = $window.document.body
|
||||||
|
redrawService = apiRedraw($window, throttleMock.throttle)
|
||||||
redrawService = apiRedraw($window)
|
|
||||||
mount = apiMounter(redrawService)
|
mount = apiMounter(redrawService)
|
||||||
render = coreRenderer($window).render
|
render = coreRenderer($window).render
|
||||||
})
|
})
|
||||||
|
|
||||||
|
o.afterEach(function() {
|
||||||
|
o(throttleMock.queueLength()).equals(0)
|
||||||
|
})
|
||||||
|
|
||||||
o("throws on invalid component", function() {
|
o("throws on invalid component", function() {
|
||||||
var threw = false
|
var threw = false
|
||||||
try {
|
try {
|
||||||
|
|
@ -69,7 +74,7 @@ o.spec("mount", function() {
|
||||||
o(root.childNodes.length).equals(0)
|
o(root.childNodes.length).equals(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
o("redraws on events", function(done) {
|
o("redraws on events", function() {
|
||||||
var onupdate = o.spy()
|
var onupdate = o.spy()
|
||||||
var oninit = o.spy()
|
var oninit = o.spy()
|
||||||
var onclick = o.spy()
|
var onclick = o.spy()
|
||||||
|
|
@ -97,17 +102,12 @@ o.spec("mount", function() {
|
||||||
o(onclick.args[0].type).equals("click")
|
o(onclick.args[0].type).equals("click")
|
||||||
o(onclick.args[0].target).equals(root.firstChild)
|
o(onclick.args[0].target).equals(root.firstChild)
|
||||||
|
|
||||||
// Wrapped to give time for the rate-limited redraw to fire
|
throttleMock.fire()
|
||||||
setTimeout(function() {
|
|
||||||
o(onupdate.callCount).equals(1)
|
|
||||||
|
|
||||||
done()
|
o(onupdate.callCount).equals(1)
|
||||||
}, FRAME_BUDGET)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
o("redraws several mount points on events", function(done, timeout) {
|
o("redraws several mount points on events", function() {
|
||||||
timeout(60)
|
|
||||||
|
|
||||||
var onupdate0 = o.spy()
|
var onupdate0 = o.spy()
|
||||||
var oninit0 = o.spy()
|
var oninit0 = o.spy()
|
||||||
var onclick0 = o.spy()
|
var onclick0 = o.spy()
|
||||||
|
|
@ -154,26 +154,26 @@ o.spec("mount", function() {
|
||||||
o(onclick0.callCount).equals(1)
|
o(onclick0.callCount).equals(1)
|
||||||
o(onclick0.this).equals(root.childNodes[0].firstChild)
|
o(onclick0.this).equals(root.childNodes[0].firstChild)
|
||||||
|
|
||||||
setTimeout(function() {
|
throttleMock.fire()
|
||||||
o(onupdate0.callCount).equals(1)
|
|
||||||
o(onupdate1.callCount).equals(1)
|
|
||||||
|
|
||||||
root.childNodes[1].firstChild.dispatchEvent(e)
|
o(onupdate0.callCount).equals(1)
|
||||||
o(onclick1.callCount).equals(1)
|
o(onupdate1.callCount).equals(1)
|
||||||
o(onclick1.this).equals(root.childNodes[1].firstChild)
|
|
||||||
|
|
||||||
setTimeout(function() {
|
root.childNodes[1].firstChild.dispatchEvent(e)
|
||||||
o(onupdate0.callCount).equals(2)
|
|
||||||
o(onupdate1.callCount).equals(2)
|
|
||||||
|
|
||||||
done()
|
o(onclick1.callCount).equals(1)
|
||||||
}, FRAME_BUDGET)
|
o(onclick1.this).equals(root.childNodes[1].firstChild)
|
||||||
}, FRAME_BUDGET)
|
|
||||||
|
|
||||||
|
throttleMock.fire()
|
||||||
|
|
||||||
|
o(onupdate0.callCount).equals(2)
|
||||||
|
o(onupdate1.callCount).equals(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
o("event handlers can skip redraw", function(done) {
|
o("event handlers can skip redraw", function() {
|
||||||
var onupdate = o.spy()
|
var onupdate = o.spy(function(){
|
||||||
|
throw new Error("This shouldn't have been called")
|
||||||
|
})
|
||||||
var oninit = o.spy()
|
var oninit = o.spy()
|
||||||
var e = $window.document.createEvent("MouseEvents")
|
var e = $window.document.createEvent("MouseEvents")
|
||||||
|
|
||||||
|
|
@ -195,15 +195,12 @@ o.spec("mount", function() {
|
||||||
|
|
||||||
o(oninit.callCount).equals(1)
|
o(oninit.callCount).equals(1)
|
||||||
|
|
||||||
// Wrapped to ensure no redraw fired
|
throttleMock.fire()
|
||||||
setTimeout(function() {
|
|
||||||
o(onupdate.callCount).equals(0)
|
|
||||||
|
|
||||||
done()
|
o(onupdate.callCount).equals(0)
|
||||||
}, FRAME_BUDGET)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
o("redraws when the render function is run", function(done) {
|
o("redraws when the render function is run", function() {
|
||||||
var onupdate = o.spy()
|
var onupdate = o.spy()
|
||||||
var oninit = o.spy()
|
var oninit = o.spy()
|
||||||
|
|
||||||
|
|
@ -221,17 +218,12 @@ o.spec("mount", function() {
|
||||||
|
|
||||||
redrawService.redraw()
|
redrawService.redraw()
|
||||||
|
|
||||||
// Wrapped to give time for the rate-limited redraw to fire
|
throttleMock.fire()
|
||||||
setTimeout(function() {
|
|
||||||
o(onupdate.callCount).equals(1)
|
|
||||||
|
|
||||||
done()
|
o(onupdate.callCount).equals(1)
|
||||||
}, FRAME_BUDGET)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
o("throttles", function(done, timeout) {
|
o("throttles", function() {
|
||||||
timeout(200)
|
|
||||||
|
|
||||||
var i = 0
|
var i = 0
|
||||||
mount(root, createComponent({view: function() {i++}}))
|
mount(root, createComponent({view: function() {i++}}))
|
||||||
var before = i
|
var before = i
|
||||||
|
|
@ -243,12 +235,11 @@ o.spec("mount", function() {
|
||||||
|
|
||||||
var after = i
|
var after = i
|
||||||
|
|
||||||
setTimeout(function(){
|
throttleMock.fire()
|
||||||
o(before).equals(1) // mounts synchronously
|
|
||||||
o(after).equals(1) // throttles rest
|
o(before).equals(1) // mounts synchronously
|
||||||
o(i).equals(2)
|
o(after).equals(1) // throttles rest
|
||||||
done()
|
o(i).equals(2)
|
||||||
},40)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
var o = require("../../ospec/ospec")
|
var o = require("../../ospec/ospec")
|
||||||
var domMock = require("../../test-utils/domMock")
|
var domMock = require("../../test-utils/domMock")
|
||||||
|
var throttleMocker = require("../../test-utils/throttleMock")
|
||||||
var apiRedraw = require("../../api/redraw")
|
var apiRedraw = require("../../api/redraw")
|
||||||
|
|
||||||
o.spec("redrawService", function() {
|
o.spec("redrawService", function() {
|
||||||
|
|
@ -17,25 +18,39 @@ o.spec("redrawService", function() {
|
||||||
redrawService.redraw()
|
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) {
|
o("should run a single renderer entry", function(done) {
|
||||||
var spy = o.spy()
|
var spy = o.spy()
|
||||||
|
|
||||||
redrawService.subscribe(root, spy)
|
redrawService.subscribe(root, spy)
|
||||||
|
|
||||||
o(spy.callCount).equals(0)
|
o(spy.callCount).equals(0)
|
||||||
|
|
||||||
redrawService.redraw()
|
|
||||||
|
|
||||||
o(spy.callCount).equals(1)
|
|
||||||
|
|
||||||
redrawService.redraw()
|
redrawService.redraw()
|
||||||
redrawService.redraw()
|
redrawService.redraw()
|
||||||
redrawService.redraw()
|
redrawService.redraw()
|
||||||
|
|
||||||
o(spy.callCount).equals(1)
|
o(spy.callCount).equals(0)
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
o(spy.callCount).equals(2)
|
o(spy.callCount).equals(1)
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}, 20)
|
}, 20)
|
||||||
})
|
})
|
||||||
|
|
@ -54,27 +69,29 @@ o.spec("redrawService", function() {
|
||||||
|
|
||||||
redrawService.redraw()
|
redrawService.redraw()
|
||||||
|
|
||||||
o(spy1.callCount).equals(1)
|
o(spy1.callCount).equals(0)
|
||||||
o(spy2.callCount).equals(1)
|
o(spy2.callCount).equals(0)
|
||||||
o(spy3.callCount).equals(1)
|
o(spy3.callCount).equals(0)
|
||||||
|
|
||||||
redrawService.redraw()
|
redrawService.redraw()
|
||||||
|
|
||||||
o(spy1.callCount).equals(1)
|
o(spy1.callCount).equals(0)
|
||||||
o(spy2.callCount).equals(1)
|
o(spy2.callCount).equals(0)
|
||||||
o(spy3.callCount).equals(1)
|
o(spy3.callCount).equals(0)
|
||||||
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
o(spy1.callCount).equals(2)
|
o(spy1.callCount).equals(1)
|
||||||
o(spy2.callCount).equals(2)
|
o(spy2.callCount).equals(1)
|
||||||
o(spy3.callCount).equals(2)
|
o(spy3.callCount).equals(1)
|
||||||
|
|
||||||
done()
|
done()
|
||||||
}, 20)
|
}, 20)
|
||||||
})
|
})
|
||||||
|
|
||||||
o("should stop running after unsubscribe", function() {
|
o("should stop running after unsubscribe", function(done) {
|
||||||
var spy = o.spy()
|
var spy = o.spy(function() {
|
||||||
|
throw new Error("This shouldn't have been called")
|
||||||
|
})
|
||||||
|
|
||||||
redrawService.subscribe(root, spy)
|
redrawService.subscribe(root, spy)
|
||||||
redrawService.unsubscribe(root, spy)
|
redrawService.unsubscribe(root, spy)
|
||||||
|
|
@ -82,9 +99,14 @@ o.spec("redrawService", function() {
|
||||||
redrawService.redraw()
|
redrawService.redraw()
|
||||||
|
|
||||||
o(spy.callCount).equals(0)
|
o(spy.callCount).equals(0)
|
||||||
|
setTimeout(function() {
|
||||||
|
o(spy.callCount).equals(0)
|
||||||
|
|
||||||
|
done()
|
||||||
|
}, 20)
|
||||||
})
|
})
|
||||||
|
|
||||||
o("does nothing on invalid unsubscribe", function() {
|
o("does nothing on invalid unsubscribe", function(done) {
|
||||||
var spy = o.spy()
|
var spy = o.spy()
|
||||||
|
|
||||||
redrawService.subscribe(root, spy)
|
redrawService.subscribe(root, spy)
|
||||||
|
|
@ -92,6 +114,10 @@ o.spec("redrawService", function() {
|
||||||
|
|
||||||
redrawService.redraw()
|
redrawService.redraw()
|
||||||
|
|
||||||
o(spy.callCount).equals(1)
|
setTimeout(function() {
|
||||||
|
o(spy.callCount).equals(1)
|
||||||
|
|
||||||
|
done()
|
||||||
|
}, 20)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
var o = require("../../ospec/ospec")
|
var o = require("../../ospec/ospec")
|
||||||
var callAsync = require("../../test-utils/callAsync")
|
var callAsync = require("../../test-utils/callAsync")
|
||||||
var browserMock = require("../../test-utils/browserMock")
|
var browserMock = require("../../test-utils/browserMock")
|
||||||
|
var throttleMocker = require("../../test-utils/throttleMock")
|
||||||
|
|
||||||
var m = require("../../render/hyperscript")
|
var m = require("../../render/hyperscript")
|
||||||
var callAsync = require("../../test-utils/callAsync")
|
var callAsync = require("../../test-utils/callAsync")
|
||||||
|
|
@ -15,18 +16,23 @@ o.spec("route", function() {
|
||||||
void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) {
|
void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) {
|
||||||
o.spec("using prefix `" + prefix + "` starting on " + env.protocol + "//" + env.hostname, function() {
|
o.spec("using prefix `" + prefix + "` starting on " + env.protocol + "//" + env.hostname, function() {
|
||||||
var FRAME_BUDGET = Math.floor(1000 / 60)
|
var FRAME_BUDGET = Math.floor(1000 / 60)
|
||||||
var $window, root, redrawService, route
|
var $window, root, redrawService, route, throttleMock
|
||||||
|
|
||||||
o.beforeEach(function() {
|
o.beforeEach(function() {
|
||||||
$window = browserMock(env)
|
$window = browserMock(env)
|
||||||
|
throttleMock = throttleMocker()
|
||||||
|
|
||||||
root = $window.document.body
|
root = $window.document.body
|
||||||
|
|
||||||
redrawService = apiRedraw($window)
|
redrawService = apiRedraw($window, throttleMock.throttle)
|
||||||
route = apiRouter($window, redrawService)
|
route = apiRouter($window, redrawService)
|
||||||
route.prefix(prefix)
|
route.prefix(prefix)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
o.afterEach(function() {
|
||||||
|
o(throttleMock.queueLength()).equals(0)
|
||||||
|
})
|
||||||
|
|
||||||
o("throws on invalid `root` DOM node", function() {
|
o("throws on invalid `root` DOM node", function() {
|
||||||
var threw = false
|
var threw = false
|
||||||
try {
|
try {
|
||||||
|
|
@ -50,7 +56,7 @@ o.spec("route", function() {
|
||||||
o(root.firstChild.nodeName).equals("DIV")
|
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()
|
var view = o.spy()
|
||||||
|
|
||||||
$window.location.href = prefix + "/"
|
$window.location.href = prefix + "/"
|
||||||
|
|
@ -60,11 +66,14 @@ o.spec("route", function() {
|
||||||
|
|
||||||
redrawService.redraw()
|
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 view = o.spy()
|
||||||
|
|
||||||
var Cmp = function(){}
|
var Cmp = function(){}
|
||||||
|
|
@ -77,11 +86,14 @@ o.spec("route", function() {
|
||||||
|
|
||||||
redrawService.redraw()
|
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()
|
var view = o.spy()
|
||||||
|
|
||||||
function Cmp() {return {view: view}}
|
function Cmp() {return {view: view}}
|
||||||
|
|
@ -93,8 +105,11 @@ o.spec("route", function() {
|
||||||
|
|
||||||
redrawService.redraw()
|
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) {
|
o("default route doesn't break back button", function(done) {
|
||||||
|
|
@ -160,11 +175,12 @@ o.spec("route", function() {
|
||||||
o(oninit.callCount).equals(1)
|
o(oninit.callCount).equals(1)
|
||||||
|
|
||||||
redrawService.redraw()
|
redrawService.redraw()
|
||||||
|
throttleMock.fire()
|
||||||
|
|
||||||
o(onupdate.callCount).equals(1)
|
o(onupdate.callCount).equals(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
o("redraws on events", function(done) {
|
o("redraws on events", function() {
|
||||||
var onupdate = o.spy()
|
var onupdate = o.spy()
|
||||||
var oninit = o.spy()
|
var oninit = o.spy()
|
||||||
var onclick = o.spy()
|
var onclick = o.spy()
|
||||||
|
|
@ -194,12 +210,9 @@ o.spec("route", function() {
|
||||||
o(onclick.args[0].type).equals("click")
|
o(onclick.args[0].type).equals("click")
|
||||||
o(onclick.args[0].target).equals(root.firstChild)
|
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) {
|
o("event handlers can skip redraw", function(done) {
|
||||||
|
|
@ -500,7 +513,10 @@ o.spec("route", function() {
|
||||||
o(oninit.callCount).equals(1)
|
o(oninit.callCount).equals(1)
|
||||||
route.set("/def")
|
route.set("/def")
|
||||||
callAsync(function() {
|
callAsync(function() {
|
||||||
|
throttleMock.fire()
|
||||||
|
|
||||||
o(oninit.callCount).equals(2)
|
o(oninit.callCount).equals(2)
|
||||||
|
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -536,23 +552,28 @@ o.spec("route", function() {
|
||||||
route(root, "/a", {
|
route(root, "/a", {
|
||||||
"/a" : {
|
"/a" : {
|
||||||
render: function() {
|
render: function() {
|
||||||
return m("div")
|
return m("div", m('p'))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"/b" : {
|
"/b" : {
|
||||||
render: function() {
|
render: function() {
|
||||||
return m("div")
|
return m("div", m('a'))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
var dom = root.firstChild
|
var dom = root.firstChild
|
||||||
|
var child = dom.firstChild
|
||||||
|
|
||||||
o(root.firstChild.nodeName).equals("DIV")
|
o(root.firstChild.nodeName).equals("DIV")
|
||||||
|
|
||||||
route.set("/b")
|
route.set("/b")
|
||||||
|
|
||||||
callAsync(function() {
|
callAsync(function() {
|
||||||
|
throttleMock.fire()
|
||||||
|
|
||||||
o(root.firstChild).equals(dom)
|
o(root.firstChild).equals(dom)
|
||||||
|
o(root.firstChild.firstChild).notEquals(child)
|
||||||
|
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
|
|
@ -586,6 +607,7 @@ o.spec("route", function() {
|
||||||
o(renderCount).equals(1)
|
o(renderCount).equals(1)
|
||||||
|
|
||||||
redrawService.redraw()
|
redrawService.redraw()
|
||||||
|
throttleMock.fire()
|
||||||
|
|
||||||
o(matchCount).equals(1)
|
o(matchCount).equals(1)
|
||||||
o(renderCount).equals(2)
|
o(renderCount).equals(2)
|
||||||
|
|
@ -621,6 +643,7 @@ o.spec("route", function() {
|
||||||
o(renderCount).equals(1)
|
o(renderCount).equals(1)
|
||||||
|
|
||||||
redrawService.redraw()
|
redrawService.redraw()
|
||||||
|
throttleMock.fire()
|
||||||
|
|
||||||
o(matchCount).equals(1)
|
o(matchCount).equals(1)
|
||||||
o(renderCount).equals(2)
|
o(renderCount).equals(2)
|
||||||
|
|
@ -815,10 +838,14 @@ o.spec("route", function() {
|
||||||
})
|
})
|
||||||
|
|
||||||
callAsync(function() {
|
callAsync(function() {
|
||||||
route.set("/b")
|
throttleMock.fire()
|
||||||
|
|
||||||
|
route.set('/b')
|
||||||
callAsync(function() {
|
callAsync(function() {
|
||||||
callAsync(function() {
|
callAsync(function() {
|
||||||
callAsync(function() {
|
callAsync(function() {
|
||||||
|
throttleMock.fire()
|
||||||
|
|
||||||
o(render.callCount).equals(0)
|
o(render.callCount).equals(0)
|
||||||
o(component.view.callCount).equals(2)
|
o(component.view.callCount).equals(2)
|
||||||
|
|
||||||
|
|
@ -939,6 +966,7 @@ o.spec("route", function() {
|
||||||
o(onmatch.callCount).equals(1)
|
o(onmatch.callCount).equals(1)
|
||||||
|
|
||||||
redrawService.redraw()
|
redrawService.redraw()
|
||||||
|
throttleMock.fire()
|
||||||
|
|
||||||
o(view.callCount).equals(2)
|
o(view.callCount).equals(2)
|
||||||
o(onmatch.callCount).equals(1)
|
o(onmatch.callCount).equals(1)
|
||||||
|
|
@ -1017,6 +1045,8 @@ o.spec("route", function() {
|
||||||
})
|
})
|
||||||
|
|
||||||
callAsync(function() {
|
callAsync(function() {
|
||||||
|
throttleMock.fire()
|
||||||
|
|
||||||
o(onmatch.callCount).equals(1)
|
o(onmatch.callCount).equals(1)
|
||||||
o(render.callCount).equals(1)
|
o(render.callCount).equals(1)
|
||||||
|
|
||||||
|
|
@ -1024,6 +1054,8 @@ o.spec("route", function() {
|
||||||
|
|
||||||
callAsync(function() {
|
callAsync(function() {
|
||||||
callAsync(function() {
|
callAsync(function() {
|
||||||
|
throttleMock.fire()
|
||||||
|
|
||||||
o(onmatch.callCount).equals(2)
|
o(onmatch.callCount).equals(2)
|
||||||
o(render.callCount).equals(2)
|
o(render.callCount).equals(2)
|
||||||
|
|
||||||
|
|
@ -1074,9 +1106,15 @@ o.spec("route", function() {
|
||||||
route.set("/b")
|
route.set("/b")
|
||||||
|
|
||||||
callAsync(function() {
|
callAsync(function() {
|
||||||
route.set("/a")
|
throttleMock.fire()
|
||||||
|
|
||||||
|
o(root.firstChild.nodeName).equals("B")
|
||||||
|
|
||||||
|
route.set('/a')
|
||||||
|
|
||||||
callAsync(function() {
|
callAsync(function() {
|
||||||
|
throttleMock.fire()
|
||||||
|
|
||||||
o(root.firstChild.nodeName).equals("A")
|
o(root.firstChild.nodeName).equals("A")
|
||||||
|
|
||||||
done()
|
done()
|
||||||
|
|
@ -1141,7 +1179,9 @@ o.spec("route", function() {
|
||||||
|
|
||||||
route.set("/b")
|
route.set("/b")
|
||||||
|
|
||||||
|
// setting the route is asynchronous
|
||||||
callAsync(function() {
|
callAsync(function() {
|
||||||
|
throttleMock.fire()
|
||||||
o(spy.callCount).equals(1)
|
o(spy.callCount).equals(1)
|
||||||
|
|
||||||
done()
|
done()
|
||||||
|
|
@ -1181,9 +1221,7 @@ o.spec("route", function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
o("throttles", function(done, timeout) {
|
o("throttles", function() {
|
||||||
timeout(200)
|
|
||||||
|
|
||||||
var i = 0
|
var i = 0
|
||||||
$window.location.href = prefix + "/"
|
$window.location.href = prefix + "/"
|
||||||
route(root, "/", {
|
route(root, "/", {
|
||||||
|
|
@ -1197,12 +1235,11 @@ o.spec("route", function() {
|
||||||
redrawService.redraw()
|
redrawService.redraw()
|
||||||
var after = i
|
var after = i
|
||||||
|
|
||||||
setTimeout(function() {
|
throttleMock.fire()
|
||||||
o(before).equals(1) // routes synchronously
|
|
||||||
o(after).equals(2) // redraws synchronously
|
o(before).equals(1) // routes synchronously
|
||||||
o(i).equals(3) // throttles rest
|
o(after).equals(1) // redraws asynchronously
|
||||||
done()
|
o(i).equals(2)
|
||||||
}, FRAME_BUDGET * 2)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
o("m.route.param is available outside of route handlers", function(done) {
|
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.
|
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
|
##### 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)`
|
`m.route.set(path, data, options)`
|
||||||
|
|
||||||
|
|
|
||||||
89
test-utils/tests/test-throttleMock.js
Normal file
89
test-utils/tests/test-throttleMock.js
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
})
|
||||||
24
test-utils/throttleMock.js
Normal file
24
test-utils/throttleMock.js
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
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() {
|
||||||
|
queue.forEach(function(fn) {fn()})
|
||||||
|
queue.length = 0
|
||||||
|
},
|
||||||
|
queueLength: function(){
|
||||||
|
return queue.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -163,8 +163,10 @@ o.spec("api", function() {
|
||||||
var count = 0
|
var count = 0
|
||||||
var root = window.document.createElement("div")
|
var root = window.document.createElement("div")
|
||||||
m.mount(root, createComponent({view: function() {count++}}))
|
m.mount(root, createComponent({view: function() {count++}}))
|
||||||
|
o(count).equals(1)
|
||||||
|
m.redraw()
|
||||||
|
o(count).equals(1)
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
m.redraw()
|
|
||||||
|
|
||||||
o(count).equals(2)
|
o(count).equals(2)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue