From a74fd19cc133e22c4f525bc8df59076a749f24ab Mon Sep 17 00:00:00 2001 From: Zolmeister Date: Sat, 16 Aug 2014 14:18:48 -0700 Subject: [PATCH] doc: updated docs for m.deferred() m.prop() and m.redraw() --- docs/mithril.deferred.md | 120 +++++++-------------------------------- docs/mithril.prop.md | 32 ++++++++--- docs/mithril.redraw.md | 28 +++++---- 3 files changed, 65 insertions(+), 115 deletions(-) diff --git a/docs/mithril.deferred.md b/docs/mithril.deferred.md index 4141b128..f97f7f71 100644 --- a/docs/mithril.deferred.md +++ b/docs/mithril.deferred.md @@ -68,11 +68,11 @@ setTimeout(function() { //asynchronous service var greetAsync = function() { m.startComputation(); - + var deferred = m.deferred(); setTimeout(function() { deferred.resolve("hello"); - + m.endComputation(); }, 1000); return deferred.promise; @@ -81,80 +81,10 @@ var greetAsync = function() { --- -### Recoverable vs Unrecoverable errors - -Recoverable errors are exceptions that are deliberately thrown by the application developer to signal an abnormal condition in the business logic. You can throw errors in this way if a computation needs to forward a failure condition to downstream promises. - -In the example below we throw a recoverable error. It rejects the subsequent promise, and logs the string `"error"` to the console - -```javascript -//standalone usage -var greetAsync = function() { - var deferred = m.deferred(); - setTimeout(function() { - deferred.resolve("hello"); - }, 1000); - return deferred.promise; -}; - -greetAsync() - .then(function(data) { - if (data != "hi") throw new Error("wrong greeting") - }) - .then(function() { - console.log("success") - }, function() { - console.log("error") - }) -``` - -Unrecoverable errors are exceptions that happen at runtime due to bugs in the code. - -In the example below, calling the inexistent `foo.bar.baz` triggers a `ReferenceError`. Mithril does not handle this error in any way: it aborts execution and dumps the error information in the console. - -```javascript -//standalone usage -var greetAsync = function() { - var deferred = m.deferred(); - setTimeout(function() { - deferred.resolve("hello"); - }, 1000); - return deferred.promise; -}; - -greetAsync().then(function() { - foo.bar.baz() -}) -``` - ---- - ### Differences from Promises/A+ -For the most part, Mithril promises behave as you'd expect a [Promise/A+](http://promises-aplus.github.io/promises-spec/) promise to behave, but with a few key differences: - -Mithril promises forward a value downstream if a resolution callback returns `undefined`. This allows simpler debugging of promise chains: - -```javascript -//a FP-friendly console.log -var log = function(value) { - console.log(value) -} - -var data = m.request({method: "GET", url: "/data"}) - .then(log) //Mithril promises let us debug like this - .then(doStuff) - -var data = m.request({method: "GET", url: "/data"}) - .then(function(value) { - console.log(value) // here's the debugging snippet - return value // Promises/A+ requires us to return a value - }) - .then(doStuff) // or else `doStuff` will break - -``` - -Another subtle difference is that the Promises/A+ require a callback to run in a different execution context than its respective `then` method. This requirement exists to support an obscure edge cases and incurs [a significant performance hit on each link of a promise chain](http://thanpol.as/javascript/promises-a-performance-hits-you-should-be-aware-of/). To be more specific, the performance hit can come either in the form of a 4ms minimum delay (if the implementation uses `setTimeout`), or from having to load a [bunch of hacky polyfill code](https://raw.githubusercontent.com/NobleJS/setImmediate/master/setImmediate.js) for a [feature that is not being considered for addition by some browser vendors](https://developer.mozilla.org/en-US/docs/Web/API/Window.setImmediate). +For the most part, Mithril promises behave as you'd expect a [Promise/A+](http://promises-aplus.github.io/promises-spec/) promise to behave, but has one difference: +Mithril promises atempt to execute synchronously if possible. To illustrate the difference between Mithril and A+ promises, consider the code below: @@ -172,8 +102,6 @@ console.log(2) In the example above, A+ promises are required to log `2` before logging `1`, whereas Mithril logs `1` before `2`. Typically `resolve`/`reject` are called asynchronously after the `then` method is called, so normally this difference does not matter. -One final difference is in how Mithril handles exceptions: if exceptions are thrown from within a success or error callbacks, A+ promises always reject the downstreams (and thus swallow the exception). Mithril does so only with errors that are subclasses of the Error class. Errors that are instances of the Error class itself are not caught by Mithril. This allows unrecoverable runtime errors to get thrown to the console w/ standard stack traces, while allowing developers to create application-space errors normally. - --- ### Signature @@ -192,41 +120,37 @@ where: - **GetterSetter { Promise then([any successCallback(any value) [, any errorCallback(any value)]]) } promise** A promise has a method called `then` which takes two computation callbacks as parameters. - + The `then` method returns another promise whose computations (if any) receive their inputs from the parent promise's computation. - + A promise is also a getter-setter (see [`m.prop`](mithril.prop.md)). After a call to either `resolve` or `reject`, it holds the result of the parent's computation (or the `resolve`/`reject` value, if the promise has no parent promises) - + - **Promise then([any successCallback(any value) [, any errorCallback(any value)]])** - + This method accepts two callbacks which process a value passed to the `resolve` and `reject` methods, respectively, and pass the processed value to the returned promise - + - **any successCallback(any value)** (optional) - + The `successCallback` is called if `resolve` is called in the root `deferred`. - + The default value (if this parameter is falsy) is the identity function `function(value) {return value}` - + If this function returns undefined, then it passes the `value` argument to the next step in the thennable queue, if any - + - **any errorCallback(any value)** (optional) - + The `errorCallback` is called if `reject` is called in the root `deferred`. - + The default value (if this parameter is falsy) is the identity function `function(value) {return value}` - + If this function returns undefined, then it passes the `value` argument to the next step in the thennable queue, if any - + - **returns Promise promise** - **void resolve(any value)** - - This method passes a value to the `successCallback` of the deferred object's child promise - -- **void reject(any value)** - - This method passes a value to the `errorCallback` of the deferred object's child promise - - - \ No newline at end of file + This method passes a value to the `successCallback` of the deferred object's child promise + +- **void reject(any value)** + + This method passes a value to the `errorCallback` of the deferred object's child promise diff --git a/docs/mithril.prop.md b/docs/mithril.prop.md index 8d3fdb19..3d3df755 100644 --- a/docs/mithril.prop.md +++ b/docs/mithril.prop.md @@ -56,6 +56,25 @@ m.request({method: "GET", url: "/users"}) --- +### Third-party promise library support + +If a promise is passed into `m.prop()`, its value will populate the prop after resolution. +Until the promise is resolved, the value of the prop will resolve to `undefined` + +Example using [Q](https://github.com/kriskowal/q) + +```javascript +var deferred = Q.defer() +var users = m.prop(deferred.promise) + +users() // undefined + +deferred.resolve("Hello") +users() // Hello +``` + +--- + ### Serializing getter-setters Getter-setters are JSON-serializable: @@ -83,18 +102,17 @@ where: - **any initialValue** (optional) An initialization value. If not provided, the value of the getter-setter's internal store defaults to `undefined`. - + - **returns any getterSetter([any value])** A getter-setter method. - + - **any value** (optional) - + If provided, it updates the getter-setter's internal store to the provided value. - + If not provided, return the current internally stored value. - + - **returns any value** - + This method always returns the value of the internal store, regardless of whether it was updated or not. - \ No newline at end of file diff --git a/docs/mithril.redraw.md b/docs/mithril.redraw.md index af80ec0c..188c9220 100644 --- a/docs/mithril.redraw.md +++ b/docs/mithril.redraw.md @@ -12,7 +12,7 @@ Note that calling this method will not do anything if a module was not activated If there are pending [`m.request`](mithril.request.md) calls in either a controller constructor or event handler, the auto-redrawing system waits for all the AJAX requests to complete before calling `m.redraw`. -This method may also be called manually from within a controller if more granular updates to the view are needed, however doing so is generally not recommended, as it may degrade performance. Model classes should never call this method. +This method may also be called manually from within a controller if more granular updates to the view are needed, however doing so is generally not recommended, as it may degrade performance. Model classes should never call this method. If you are developing an asynchronous model-level service and finding that Mithril is not redrawing the view after your code runs, you should use [`m.startComputation` and `m.endComputation`](mithril.computation.md) to integrate with Mithril's auto-redrawing system instead. @@ -52,12 +52,22 @@ m("input", {onkeydown: function(e) { --- +### Forcing redraw + +If you find yourself needing to redraw before the browsers normal redraw cycle, you can force it. + +```javascript +m.redraw(true) // force +``` + +--- + ### Signature [How to read signatures](how-to-read-signatures.md) ```clike -void redraw() { GetterSetter strategy } +void redraw([Boolean force=false]) { GetterSetter strategy } where: GetterSetter :: String getterSetter([String value]) @@ -66,17 +76,15 @@ where: - ### m.redraw.strategy - + **GetterSetter strategy** - + The `m.redraw.strategy` getter-setter indicates how the next module redraw will occur. It can be one of three values: - + - `"all"` - recreates the DOM tree from scratch - `"diff"` - updates only DOM elements if needed - `"none"` - leaves the DOM tree intact - - This value can be programmatically changed in controllers and event handlers to modify the next redrawing strategy. It is modified internally by Mithril to the value `"all"` before running controller constructors, and to the value `"diff"` after all redraws. - - Calling this function without arguments returns the currently assigned redraw strategy. - \ No newline at end of file + This value can be programmatically changed in controllers and event handlers to modify the next redrawing strategy. It is modified internally by Mithril to the value `"all"` before running controller constructors, and to the value `"diff"` after all redraws. + + Calling this function without arguments returns the currently assigned redraw strategy.