expose exception monitor
This commit is contained in:
parent
69ead12a1d
commit
090aefbc07
3 changed files with 54 additions and 12 deletions
|
|
@ -6,6 +6,7 @@
|
|||
[Retrieving a value via the getter-setter API](#retrieving-a-value-via-the-getter-setter-api)
|
||||
[Integrating to the Mithril redrawing system](#integrating-to-the-mithril-redrawing-system)
|
||||
[Differences from Promises/A+](#differences-from-promises-a-)
|
||||
[The exception monitor](#the-exception-monitor)
|
||||
[Signature](#signature)
|
||||
|
||||
---
|
||||
|
|
@ -143,7 +144,7 @@ There are a couple of reasons why Mithril runs callbacks synchronously. Conformi
|
|||
|
||||
#### Unchecked Error Handling
|
||||
|
||||
Mithril does not swallow errors if these errors are subclasses of the Error class. Manually throwing an instance of the Error class itself (or any other objects or primitives) does trigger the rejection callback path as per the Promises/A+ spec.
|
||||
By default, Mithril does not swallow errors if these errors are subclasses of the Error class. Manually throwing an instance of the Error class itself (or any other objects or primitives) does trigger the rejection callback path as per the Promises/A+ spec.
|
||||
|
||||
This deviation from the spec is there to make it easier for developers to find common logical errors such as typos that lead to null reference exceptions. By default, the spec requires that all thrown errors trigger rejection, which result in silent failures if the developer forgets to explicitly handle the failure case.
|
||||
|
||||
|
|
@ -151,15 +152,24 @@ For example, there is simply never a case where a developer would want to progra
|
|||
|
||||
The other side of the coin is still supported: if a developer needs to signal an exceptional condition within a promise callback, they can manually throw a `new Error` (for example, if a validation rule failed, and there should be an error message displayed to the user).
|
||||
|
||||
Note that the default promise exception handling semantics can be modified. See the next section.
|
||||
|
||||
---
|
||||
|
||||
### Replacing the built-in Promise implementation
|
||||
### The exception monitor
|
||||
|
||||
If strict adherence to the Promises/A+ spec is required, Mithril allows its built-in implementation to be swapped out. Here's how one would configure Mithril to use the ES6 Promise class that ships with Chrome and Firefox:
|
||||
Any time an exception is thrown inside a promise callback, Mithril calls `m.deferred.onerror(e)`.
|
||||
|
||||
```javascript
|
||||
//use ES6 Promises as the promise engine
|
||||
m.deferred.constructor = Promise
|
||||
By default, this event handler rethrows the exception to the console if an error is a subclass of Error (but not an instance of Error itself). Otherwise it follows the Promises/A+ specifications. It does this because developers expect unexpected errors like null reference exceptions to be thrown to the console for debugging purposes, and these errors are always subclasses of Error. On the other hand, javascript developers rarely ever throw errors that are subclasses of Error.
|
||||
|
||||
The `onerror` function can be safely replaced if the default error monitoring semantics are not desired.
|
||||
|
||||
```
|
||||
//swallow all errors
|
||||
m.deferred.onerror = function() {}
|
||||
|
||||
//only log errors
|
||||
m.deferred.onerror = function(e) {console.error(e)}
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -169,7 +179,7 @@ m.deferred.constructor = Promise
|
|||
[How to read signatures](how-to-read-signatures.md)
|
||||
|
||||
```clike
|
||||
Deferred deferred()
|
||||
Deferred deferred() {void onerror(Error e)}
|
||||
|
||||
where:
|
||||
Deferred :: Object { Promise promise, void resolve(any value), void reject(any value) }
|
||||
|
|
@ -214,3 +224,7 @@ where:
|
|||
- **void reject(any value)**
|
||||
|
||||
This method passes a value to the `errorCallback` of the deferred object's child promise
|
||||
|
||||
- **static void onerror(Error e)**
|
||||
|
||||
This method gets called every time an exception is thrown inside a promise callback. By default, it rethrows to the console if an error is a subclass of Error (but not an instance of Error itself). Otherwise it follows the Promises/A+ specifications.
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
[Using Different Data Transfer Formats](#using-different-data-transfer-formats)
|
||||
[Using variable data formats](#using-variable-data-formats)
|
||||
[Extracting Metadata from the Response](#extracting-metadata-from-the-response)
|
||||
[Custom request rejections](#custom-request-rejections)
|
||||
[Configuring the underlying XMLHttpRequest](#configuring-the-underlying-xmlhttprequest)
|
||||
[Aborting a request](#aborting-a-request)
|
||||
[Signature](#signature)
|
||||
|
|
@ -283,6 +284,32 @@ m.request({method: "POST", url: "/foo", extract: extract});
|
|||
|
||||
---
|
||||
|
||||
### Custom request rejections
|
||||
|
||||
If you want to be able to handle a condition as an error in a promise rejection handler, you can throw an `Error` from `extract` to reject the promise.
|
||||
|
||||
This is useful, for example, if you received invalid JSON from the server in production and you want to display a message to the user saying that the server is offline.
|
||||
|
||||
```javascript
|
||||
var extract = function(xhr, xhrOptions) {
|
||||
try {
|
||||
return JSON.stringify(xhr.responseText)
|
||||
}
|
||||
catch (e) {
|
||||
//e instanceof SyntaxError == true
|
||||
//by default `e` would be caught by Mithril's promise exception monitor and rethrown to the console
|
||||
//this new error follows Promises/A+ specifications and triggers a rejection in the downstream promises without hitting the console.
|
||||
throw new Error("Server is offline")
|
||||
}
|
||||
}
|
||||
|
||||
m.request({method: "POST", url: "/foo", extract: extract});
|
||||
```
|
||||
|
||||
You can read more about the [promise exception monitor here](mithril.deferred.md#unchecked-error-handling).
|
||||
|
||||
---
|
||||
|
||||
### Configuring the underlying XMLHttpRequest
|
||||
|
||||
The `config` option can be used to arbitrarily configure the native XMLHttpRequest instance and to access properties that would not be accessible otherwise.
|
||||
|
|
|
|||
11
mithril.js
11
mithril.js
|
|
@ -703,7 +703,7 @@ Mithril = m = new function app(window, undefined) {
|
|||
})
|
||||
}
|
||||
catch (e) {
|
||||
rethrowUnchecked(e)
|
||||
m.deferred.onerror(e)
|
||||
promiseValue = e
|
||||
failureCallback()
|
||||
}
|
||||
|
|
@ -719,7 +719,7 @@ Mithril = m = new function app(window, undefined) {
|
|||
then = promiseValue && promiseValue.then
|
||||
}
|
||||
catch (e) {
|
||||
rethrowUnchecked(e)
|
||||
m.deferred.onerror(e)
|
||||
promiseValue = e
|
||||
state = REJECTING
|
||||
return fire()
|
||||
|
|
@ -741,7 +741,7 @@ Mithril = m = new function app(window, undefined) {
|
|||
}
|
||||
}
|
||||
catch (e) {
|
||||
rethrowUnchecked(e)
|
||||
m.deferred.onerror(e)
|
||||
promiseValue = e
|
||||
return finish()
|
||||
}
|
||||
|
|
@ -760,7 +760,7 @@ Mithril = m = new function app(window, undefined) {
|
|||
})
|
||||
}
|
||||
}
|
||||
function rethrowUnchecked(e) {
|
||||
m.deferred.onerror = function(e) {
|
||||
if (type.call(e) == "[object Error]" && !e.constructor.toString().match(/ Error/)) throw e
|
||||
}
|
||||
|
||||
|
|
@ -902,7 +902,8 @@ Mithril = m = new function app(window, undefined) {
|
|||
deferred[e.type == "load" ? "resolve" : "reject"](response)
|
||||
}
|
||||
catch (e) {
|
||||
if (!rethrowUnchecked(e)) deferred.reject(e)
|
||||
m.deferred.onerror(e)
|
||||
deferred.reject(e)
|
||||
}
|
||||
if (xhrOptions.background !== true) m.endComputation()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue