Skip to content
This repository has been archived by the owner on Dec 19, 2017. It is now read-only.

Commit

Permalink
Add ctx.queue
Browse files Browse the repository at this point in the history
  • Loading branch information
caseyWebb committed Jan 20, 2017
1 parent 765cf0d commit 5890ae6
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 2 deletions.
5 changes: 5 additions & 0 deletions docs/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ Adds a function to be executed before the page is navigated away from. Callbacks
are executed LIFO. Async callbacks should use the optional `done` parameter
to continue to the next callback (if any), or navigation.

#### ctx.queue(promise)
Only available in `beforeRender` [middleware](./middleware.md), this function
queues a promise and allows middleware to continue running, but still resolves
before the page is rendered.

A callback may prevent navigation by
- returning `false`
- returning a promise that a) is rejected or b) resolves `false`
Expand Down
9 changes: 7 additions & 2 deletions docs/middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ but I __highly__ discourage it. There is a better way... keep reading...

Middleware functions are passed 2 arguments:
- `ctx`: the ctx object passed into the viewmodel
- `done`: an optional callback for async functions; promises are also supported, and encouraged
- `done`: an optional callback for async functions\*; promises are also supported, and encouraged

\*that should wait for completion before continuing middleware, otherwise use
`ctx.queue()`

Let's look at some example logging middleware...

Expand All @@ -97,7 +100,9 @@ your data calls out of the viewmodel...

In the viewmodel for the `user` component, `ctx.user` will contain the user. Since
we're returning a promise, the next middleware (in this case the component setter)
will not be executed until after the call has completed.
will not be executed until after the call has completed. If you wished to continue
middleware execution immediately, but still ensure any asynchronous operations
have completed before render, you could use `ctx.queue`.

Let's see how we can take some finer control. As has been the theme, you've got options...

Expand Down
6 changes: 6 additions & 0 deletions src/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ export default class Route {
return await sequence(afterDisposes)
}

const queue = []
ctx.queue = (promise) => queue.push(promise)

const [appBeforeRender, appDownstream] = runMiddleware(Router.middleware, ctx)

afterRenders.push(appDownstream)
Expand All @@ -113,6 +116,9 @@ export default class Route {
afterDisposes.unshift(routeDownstream)

await routeBeforeRender
await Promise.all(queue)

delete ctx.queue
}

static createRoutes(routes) {
Expand Down
2 changes: 2 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import './history'
import './force-update'
import './with'
import './middleware'
import './queue'
import './before-navigate-callbacks'
import './element'
import './passthrough'
Expand All @@ -25,6 +26,7 @@ const tests = [
'anchor',
'binding',
'middleware',
'queue',
'before-navigate-callbacks',
'element',
'passthrough',
Expand Down
39 changes: 39 additions & 0 deletions test/queue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import ko from 'knockout'

ko.components.register('queue', {
template: '<ko-component-router params="routes: routes"></ko-component-router>',
viewModel: class QueueTest {
constructor({ t, next }) {
let queuedPromiseResolved = false

this.routes = {
'/': ['foo',
(ctx) =>
ctx.queue(new Promise((resolve) => {
setTimeout(() => {
queuedPromiseResolved = true
resolve()
}, 1000)
})),
() => {
t.notOk(queuedPromiseResolved, 'queued promises let middleware continue')
}
]
}

ko.components.register('foo', {
viewModel: () => {
t.ok(queuedPromiseResolved, 'queued promise resolves before component render')
next()
}
})

history.replaceState(null, null, '/')
}

dispose() {
ko.components.unregister('queue')
ko.components.unregister('foo')
}
}
})

0 comments on commit 5890ae6

Please sign in to comment.