|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- # RSVP.js [](http://travis-ci.org/tildeio/rsvp.js) [](http://inch-ci.org/github/tildeio/rsvp.js)
-
- RSVP.js provides simple tools for organizing asynchronous code.
-
- Specifically, it is a tiny implementation of Promises/A+.
-
- It works in node and the browser (IE6+, all the popular evergreen ones).
-
- ## downloads
-
- - [rsvp-latest](http://rsvpjs-builds.s3.amazonaws.com/rsvp-latest.js)
- - [rsvp-latest (minified)](http://rsvpjs-builds.s3.amazonaws.com/rsvp-latest.min.js)
-
- ## Promises
-
- Although RSVP is ES6 compliant, it does bring along some extra toys. If you would prefer a strict ES6 subset, I would suggest checking out our sibling project https://github.com/stefanpenner/es6-promise, It is RSVP but stripped down to the ES6 spec features.
-
- ## Bower
-
- `bower install -S rsvp`
-
- ## NPM
-
- `npm install --save rsvp`
-
- `RSVP.Promise` is an implementation of
- [Promises/A+](http://promises-aplus.github.com/promises-spec/) that passes the
- [test suite](https://github.com/promises-aplus/promises-tests).
-
- It delivers all promises asynchronously, even if the value is already
- available, to help you write consistent code that doesn't change if the
- underlying data provider changes from synchronous to asynchronous.
-
- It is compatible with [TaskJS](http://taskjs.org/), a library by Dave
- Herman of Mozilla that uses ES6 generators to allow you to write
- synchronous code with promises. It currently works in Firefox, and will
- work in any browser that adds support for ES6 generators. See the
- section below on TaskJS for more information.
-
- ### Basic Usage
-
- ```javascript
- var RSVP = require('rsvp');
-
- var promise = new RSVP.Promise(function(resolve, reject) {
- // succeed
- resolve(value);
- // or reject
- reject(error);
- });
-
- promise.then(function(value) {
- // success
- }).catch(function(error) {
- // failure
- });
- ```
-
- Once a promise has been resolved or rejected, it cannot be resolved or
- rejected again.
-
- Here is an example of a simple XHR2 wrapper written using RSVP.js:
-
- ```javascript
- var getJSON = function(url) {
- var promise = new RSVP.Promise(function(resolve, reject){
- var client = new XMLHttpRequest();
- client.open("GET", url);
- client.onreadystatechange = handler;
- client.responseType = "json";
- client.setRequestHeader("Accept", "application/json");
- client.send();
-
- function handler() {
- if (this.readyState === this.DONE) {
- if (this.status === 200) { resolve(this.response); }
- else { reject(this); }
- }
- };
- });
-
- return promise;
- };
-
- getJSON("/posts.json").then(function(json) {
- // continue
- }).catch(function(error) {
- // handle errors
- });
- ```
-
- ### Chaining
-
- One of the really awesome features of Promises/A+ promises are that they
- can be chained together. In other words, the return value of the first
- resolve handler will be passed to the second resolve handler.
-
- If you return a regular value, it will be passed, as is, to the next
- handler.
-
- ```javascript
- getJSON("/posts.json").then(function(json) {
- return json.post;
- }).then(function(post) {
- // proceed
- });
- ```
-
- The really awesome part comes when you return a promise from the first
- handler:
-
- ```javascript
- getJSON("/post/1.json").then(function(post) {
- // save off post
- return getJSON(post.commentURL);
- }).then(function(comments) {
- // proceed with access to post and comments
- });
- ```
-
- This allows you to flatten out nested callbacks, and is the main feature
- of promises that prevents "rightward drift" in programs with a lot of
- asynchronous code.
-
- Errors also propagate:
-
- ```javascript
- getJSON("/posts.json").then(function(posts) {
-
- }).catch(function(error) {
- // since no rejection handler was passed to the
- // first `.then`, the error propagates.
- });
- ```
-
- You can use this to emulate `try/catch` logic in synchronous code.
- Simply chain as many resolve callbacks as a you want, and add a failure
- handler at the end to catch errors.
-
- ```javascript
- getJSON("/post/1.json").then(function(post) {
- return getJSON(post.commentURL);
- }).then(function(comments) {
- // proceed with access to posts and comments
- }).catch(function(error) {
- // handle errors in either of the two requests
- });
- ```
-
- ## Error Handling
-
- There are times when dealing with promises that it seems like any errors
- are being 'swallowed', and not properly raised. This makes it extremely
- difficult to track down where a given issue is coming from. Thankfully,
- `RSVP` has a solution for this problem built in.
-
- You can register functions to be called when an uncaught error occurs
- within your promises. These callback functions can be anything, but a common
- practice is to call `console.assert` to dump the error to the console.
-
- ```javascript
- RSVP.on('error', function(reason) {
- console.assert(false, reason);
- });
- ```
-
- `RSVP` allows Promises to be labeled: `Promise.resolve(value, 'I AM A LABEL')`
- If provided, this label is passed as the second argument to `RSVP.on('error')`
-
- ```javascript
- RSVP.on('error', function(reason, label) {
- if (label) {
- console.error(label);
- }
-
- console.assert(false, reason);
- });
- ```
-
-
- **NOTE:** promises do allow for errors to be handled asynchronously, so
- this callback may result in false positives.
-
- ## Finally
-
- `finally` will be invoked regardless of the promise's fate, just as native
- try/catch/finally behaves.
-
- ```js
- findAuthor().catch(function(reason){
- return findOtherAuthor();
- }).finally(function(){
- // author was either found, or not
- });
- ```
-
-
- ## Arrays of promises
-
- Sometimes you might want to work with many promises at once. If you
- pass an array of promises to the `all()` method it will return a new
- promise that will be fulfilled when all of the promises in the array
- have been fulfilled; or rejected immediately if any promise in the array
- is rejected.
-
- ```javascript
- var promises = [2, 3, 5, 7, 11, 13].map(function(id){
- return getJSON("/post/" + id + ".json");
- });
-
- RSVP.all(promises).then(function(posts) {
- // posts contains an array of results for the given promises
- }).catch(function(reason){
- // if any of the promises fails.
- });
- ```
-
- ## Hash of promises
-
- If you need to reference many promises at once (like `all()`), but would like
- to avoid encoding the actual promise order you can use `hash()`. If you pass
- an object literal (where the values are promises) to the `hash()` method it will
- return a new promise that will be fulfilled when all of the promises have been
- fulfilled; or rejected immediately if any promise is rejected.
-
- The key difference to the `all()` function is that both the fulfillment value
- and the argument to the `hash()` function are object literals. This allows
- you to simply reference the results directly off the returned object without
- having to remember the initial order like you would with `all()`.
-
- ```javascript
- var promises = {
- posts: getJSON("/posts.json"),
- users: getJSON("/users.json")
- };
-
- RSVP.hash(promises).then(function(results) {
- console.log(results.users) // print the users.json results
- console.log(results.posts) // print the posts.json results
- });
- ```
-
- ## All settled and hash settled
-
- Sometimes you want to work with several promises at once, but instead of
- rejecting immediately if any promise is rejected, as with `all()` or `hash()`,
- you want to be able to inspect the results of all your promises, whether
- they fulfill or reject. For this purpose, you can use `allSettled()` and
- `hashSettled()`. These work exactly like `all()` and `hash()`, except that
- they fulfill with an array or hash (respectively) of the constituent promises'
- result states. Each state object will either indicate fulfillment or
- rejection, and provide the corresponding value or reason. The states will take
- one of the following formats:
-
- ```javascript
- { state: 'fulfilled', value: value }
- or
- { state: 'rejected', reason: reason }
- ```
-
- ## Deferred
-
- > The `RSVP.Promise` constructor is generally a better, less error-prone choice
- > than `RSVP.defer()`. Promises are recommended unless the specific
- > properties of deferred are needed.
-
- Sometimes one needs to create a deferred object, without immediately specifying
- how it will be resolved. These deferred objects are essentially a wrapper around
- a promise, whilst providing late access to the `resolve()` and `reject()` methods.
-
- A deferred object has this form: `{ promise, resolve(x), reject(r) }`.
-
- ```javascript
- var deferred = RSVP.defer();
- // ...
- deferred.promise // access the promise
- // ...
- deferred.resolve();
-
- ```
-
- ## TaskJS
-
- The [TaskJS](http://taskjs.org/) library makes it possible to take
- promises-oriented code and make it synchronous using ES6 generators.
-
- Let's review an earlier example:
-
- ```javascript
- getJSON("/post/1.json").then(function(post) {
- return getJSON(post.commentURL);
- }).then(function(comments) {
- // proceed with access to posts and comments
- }).catch(function(reason) {
- // handle errors in either of the two requests
- });
- ```
-
- Without any changes to the implementation of `getJSON`, you could write
- the following code with TaskJS:
-
- ```javascript
- spawn(function *() {
- try {
- var post = yield getJSON("/post/1.json");
- var comments = yield getJSON(post.commentURL);
- } catch(error) {
- // handle errors
- }
- });
- ```
-
- In the above example, `function *` is new syntax in ES6 for
- [generators](http://wiki.ecmascript.org/doku.php?id=harmony:generators).
- Inside a generator, `yield` pauses the generator, returning control to
- the function that invoked the generator. In this case, the invoker is a
- special function that understands the semantics of Promises/A, and will
- automatically resume the generator as soon as the promise is resolved.
-
- The cool thing here is the same promises that work with current
- JavaScript using `.then` will work seamlessly with TaskJS once a browser
- has implemented it!
-
- ## Instrumentation
-
- ```js
- function listener (event) {
- event.guid // guid of promise. Must be globally unique, not just within the implementation
- event.childGuid // child of child promise (for chained via `then`)
- event.eventName // one of ['created', 'chained', 'fulfilled', 'rejected']
- event.detail // fulfillment value or rejection reason, if applicable
- event.label // label passed to promise's constructor
- event.timeStamp // milliseconds elapsed since 1 January 1970 00:00:00 UTC up until now
- event.stack // stack at the time of the event. (if 'instrument-with-stack' is true)
- }
-
- RSVP.configure('instrument', true | false);
- // capturing the stacks is slow, so you also have to opt in
- RSVP.configure('instrument-with-stack', true | false);
-
- // events
- RSVP.on('created', listener);
- RSVP.on('chained', listener);
- RSVP.on('fulfilled', listener);
- RSVP.on('rejected', listener);
- ```
-
- Events are only triggered when `RSVP.configure('instrument')` is true, although
- listeners can be registered at any time.
-
- ## Building & Testing
-
- Custom tasks:
-
- * `npm test` - build & test
- * `npm test:node` - build & test just node
- * `npm test:server` - build/watch & test
- * `npm run build` - Build
- * `npm run build:production` - Build production (with minified output)
- * `npm start` - build, watch and run interactive server at http://localhost:4200'
-
- ## Releasing
-
- Check what release-it will do by running `npm run-script dry-run-release`.
- To actually release, run `node_modules/.bin/release-it`.
|