| [9af201e] | 1 | [](http://travis-ci.org/kriskowal/q)
|
|---|
| 2 | [](https://cdnjs.com/libraries/q.js)
|
|---|
| 3 |
|
|---|
| 4 | <a href="http://promises-aplus.github.com/promises-spec">
|
|---|
| 5 | <img src="http://kriskowal.github.io/q/q.png" align="right" alt="Q logo" />
|
|---|
| 6 | </a>
|
|---|
| 7 |
|
|---|
| 8 | If a function cannot return a value or throw an exception without
|
|---|
| 9 | blocking, it can return a promise instead. A promise is an object
|
|---|
| 10 | that represents the return value or the thrown exception that the
|
|---|
| 11 | function may eventually provide. A promise can also be used as a
|
|---|
| 12 | proxy for a [remote object][Q-Connection] to overcome latency.
|
|---|
| 13 |
|
|---|
| 14 | [Q-Connection]: https://github.com/kriskowal/q-connection
|
|---|
| 15 |
|
|---|
| 16 | On the first pass, promises can mitigate the “[Pyramid of
|
|---|
| 17 | Doom][POD]”: the situation where code marches to the right faster
|
|---|
| 18 | than it marches forward.
|
|---|
| 19 |
|
|---|
| 20 | [POD]: http://calculist.org/blog/2011/12/14/why-coroutines-wont-work-on-the-web/
|
|---|
| 21 |
|
|---|
| 22 | ```javascript
|
|---|
| 23 | step1(function (value1) {
|
|---|
| 24 | step2(value1, function(value2) {
|
|---|
| 25 | step3(value2, function(value3) {
|
|---|
| 26 | step4(value3, function(value4) {
|
|---|
| 27 | // Do something with value4
|
|---|
| 28 | });
|
|---|
| 29 | });
|
|---|
| 30 | });
|
|---|
| 31 | });
|
|---|
| 32 | ```
|
|---|
| 33 |
|
|---|
| 34 | With a promise library, you can flatten the pyramid.
|
|---|
| 35 |
|
|---|
| 36 | ```javascript
|
|---|
| 37 | Q.fcall(promisedStep1)
|
|---|
| 38 | .then(promisedStep2)
|
|---|
| 39 | .then(promisedStep3)
|
|---|
| 40 | .then(promisedStep4)
|
|---|
| 41 | .then(function (value4) {
|
|---|
| 42 | // Do something with value4
|
|---|
| 43 | })
|
|---|
| 44 | .catch(function (error) {
|
|---|
| 45 | // Handle any error from all above steps
|
|---|
| 46 | })
|
|---|
| 47 | .done();
|
|---|
| 48 | ```
|
|---|
| 49 |
|
|---|
| 50 | With this approach, you also get implicit error propagation, just like `try`,
|
|---|
| 51 | `catch`, and `finally`. An error in `promisedStep1` will flow all the way to
|
|---|
| 52 | the `catch` function, where it’s caught and handled. (Here `promisedStepN` is
|
|---|
| 53 | a version of `stepN` that returns a promise.)
|
|---|
| 54 |
|
|---|
| 55 | The callback approach is called an “inversion of control”.
|
|---|
| 56 | A function that accepts a callback instead of a return value
|
|---|
| 57 | is saying, “Don’t call me, I’ll call you.”. Promises
|
|---|
| 58 | [un-invert][IOC] the inversion, cleanly separating the input
|
|---|
| 59 | arguments from control flow arguments. This simplifies the
|
|---|
| 60 | use and creation of API’s, particularly variadic,
|
|---|
| 61 | rest and spread arguments.
|
|---|
| 62 |
|
|---|
| 63 | [IOC]: http://www.slideshare.net/domenicdenicola/callbacks-promises-and-coroutines-oh-my-the-evolution-of-asynchronicity-in-javascript
|
|---|
| 64 |
|
|---|
| 65 |
|
|---|
| 66 | ## Getting Started
|
|---|
| 67 |
|
|---|
| 68 | The Q module can be loaded as:
|
|---|
| 69 |
|
|---|
| 70 | - A ``<script>`` tag (creating a ``Q`` global variable): ~2.5 KB minified and
|
|---|
| 71 | gzipped.
|
|---|
| 72 | - A Node.js and CommonJS module, available in [npm](https://npmjs.org/) as
|
|---|
| 73 | the [q](https://npmjs.org/package/q) package
|
|---|
| 74 | - An AMD module
|
|---|
| 75 | - A [component](https://github.com/component/component) as ``microjs/q``
|
|---|
| 76 | - Using [bower](http://bower.io/) as `q#^1.4.1`
|
|---|
| 77 | - Using [NuGet](http://nuget.org/) as [Q](https://nuget.org/packages/q)
|
|---|
| 78 |
|
|---|
| 79 | Q can exchange promises with jQuery, Dojo, When.js, WinJS, and more.
|
|---|
| 80 |
|
|---|
| 81 | ## Resources
|
|---|
| 82 |
|
|---|
| 83 | Our [wiki][] contains a number of useful resources, including:
|
|---|
| 84 |
|
|---|
| 85 | - A method-by-method [Q API reference][reference].
|
|---|
| 86 | - A growing [examples gallery][examples], showing how Q can be used to make
|
|---|
| 87 | everything better. From XHR to database access to accessing the Flickr API,
|
|---|
| 88 | Q is there for you.
|
|---|
| 89 | - There are many libraries that produce and consume Q promises for everything
|
|---|
| 90 | from file system/database access or RPC to templating. For a list of some of
|
|---|
| 91 | the more popular ones, see [Libraries][].
|
|---|
| 92 | - If you want materials that introduce the promise concept generally, and the
|
|---|
| 93 | below tutorial isn't doing it for you, check out our collection of
|
|---|
| 94 | [presentations, blog posts, and podcasts][resources].
|
|---|
| 95 | - A guide for those [coming from jQuery's `$.Deferred`][jquery].
|
|---|
| 96 |
|
|---|
| 97 | We'd also love to have you join the Q-Continuum [mailing list][].
|
|---|
| 98 |
|
|---|
| 99 | [wiki]: https://github.com/kriskowal/q/wiki
|
|---|
| 100 | [reference]: https://github.com/kriskowal/q/wiki/API-Reference
|
|---|
| 101 | [examples]: https://github.com/kriskowal/q/wiki/Examples-Gallery
|
|---|
| 102 | [Libraries]: https://github.com/kriskowal/q/wiki/Libraries
|
|---|
| 103 | [resources]: https://github.com/kriskowal/q/wiki/General-Promise-Resources
|
|---|
| 104 | [jquery]: https://github.com/kriskowal/q/wiki/Coming-from-jQuery
|
|---|
| 105 | [mailing list]: https://groups.google.com/forum/#!forum/q-continuum
|
|---|
| 106 |
|
|---|
| 107 |
|
|---|
| 108 | ## Tutorial
|
|---|
| 109 |
|
|---|
| 110 | Promises have a ``then`` method, which you can use to get the eventual
|
|---|
| 111 | return value (fulfillment) or thrown exception (rejection).
|
|---|
| 112 |
|
|---|
| 113 | ```javascript
|
|---|
| 114 | promiseMeSomething()
|
|---|
| 115 | .then(function (value) {
|
|---|
| 116 | }, function (reason) {
|
|---|
| 117 | });
|
|---|
| 118 | ```
|
|---|
| 119 |
|
|---|
| 120 | If ``promiseMeSomething`` returns a promise that gets fulfilled later
|
|---|
| 121 | with a return value, the first function (the fulfillment handler) will be
|
|---|
| 122 | called with the value. However, if the ``promiseMeSomething`` function
|
|---|
| 123 | gets rejected later by a thrown exception, the second function (the
|
|---|
| 124 | rejection handler) will be called with the exception.
|
|---|
| 125 |
|
|---|
| 126 | Note that resolution of a promise is always asynchronous: that is, the
|
|---|
| 127 | fulfillment or rejection handler will always be called in the next turn of the
|
|---|
| 128 | event loop (i.e. `process.nextTick` in Node). This gives you a nice
|
|---|
| 129 | guarantee when mentally tracing the flow of your code, namely that
|
|---|
| 130 | ``then`` will always return before either handler is executed.
|
|---|
| 131 |
|
|---|
| 132 | In this tutorial, we begin with how to consume and work with promises. We'll
|
|---|
| 133 | talk about how to create them, and thus create functions like
|
|---|
| 134 | `promiseMeSomething` that return promises, [below](#the-beginning).
|
|---|
| 135 |
|
|---|
| 136 |
|
|---|
| 137 | ### Propagation
|
|---|
| 138 |
|
|---|
| 139 | The ``then`` method returns a promise, which in this example, I’m
|
|---|
| 140 | assigning to ``outputPromise``.
|
|---|
| 141 |
|
|---|
| 142 | ```javascript
|
|---|
| 143 | var outputPromise = getInputPromise()
|
|---|
| 144 | .then(function (input) {
|
|---|
| 145 | }, function (reason) {
|
|---|
| 146 | });
|
|---|
| 147 | ```
|
|---|
| 148 |
|
|---|
| 149 | The ``outputPromise`` variable becomes a new promise for the return
|
|---|
| 150 | value of either handler. Since a function can only either return a
|
|---|
| 151 | value or throw an exception, only one handler will ever be called and it
|
|---|
| 152 | will be responsible for resolving ``outputPromise``.
|
|---|
| 153 |
|
|---|
| 154 | - If you return a value in a handler, ``outputPromise`` will get
|
|---|
| 155 | fulfilled.
|
|---|
| 156 |
|
|---|
| 157 | - If you throw an exception in a handler, ``outputPromise`` will get
|
|---|
| 158 | rejected.
|
|---|
| 159 |
|
|---|
| 160 | - If you return a **promise** in a handler, ``outputPromise`` will
|
|---|
| 161 | “become” that promise. Being able to become a new promise is useful
|
|---|
| 162 | for managing delays, combining results, or recovering from errors.
|
|---|
| 163 |
|
|---|
| 164 | If the ``getInputPromise()`` promise gets rejected and you omit the
|
|---|
| 165 | rejection handler, the **error** will go to ``outputPromise``:
|
|---|
| 166 |
|
|---|
| 167 | ```javascript
|
|---|
| 168 | var outputPromise = getInputPromise()
|
|---|
| 169 | .then(function (value) {
|
|---|
| 170 | });
|
|---|
| 171 | ```
|
|---|
| 172 |
|
|---|
| 173 | If the input promise gets fulfilled and you omit the fulfillment handler, the
|
|---|
| 174 | **value** will go to ``outputPromise``:
|
|---|
| 175 |
|
|---|
| 176 | ```javascript
|
|---|
| 177 | var outputPromise = getInputPromise()
|
|---|
| 178 | .then(null, function (error) {
|
|---|
| 179 | });
|
|---|
| 180 | ```
|
|---|
| 181 |
|
|---|
| 182 | Q promises provide a ``fail`` shorthand for ``then`` when you are only
|
|---|
| 183 | interested in handling the error:
|
|---|
| 184 |
|
|---|
| 185 | ```javascript
|
|---|
| 186 | var outputPromise = getInputPromise()
|
|---|
| 187 | .fail(function (error) {
|
|---|
| 188 | });
|
|---|
| 189 | ```
|
|---|
| 190 |
|
|---|
| 191 | If you are writing JavaScript for modern engines only or using
|
|---|
| 192 | CoffeeScript, you may use `catch` instead of `fail`.
|
|---|
| 193 |
|
|---|
| 194 | Promises also have a ``fin`` function that is like a ``finally`` clause.
|
|---|
| 195 | The final handler gets called, with no arguments, when the promise
|
|---|
| 196 | returned by ``getInputPromise()`` either returns a value or throws an
|
|---|
| 197 | error. The value returned or error thrown by ``getInputPromise()``
|
|---|
| 198 | passes directly to ``outputPromise`` unless the final handler fails, and
|
|---|
| 199 | may be delayed if the final handler returns a promise.
|
|---|
| 200 |
|
|---|
| 201 | ```javascript
|
|---|
| 202 | var outputPromise = getInputPromise()
|
|---|
| 203 | .fin(function () {
|
|---|
| 204 | // close files, database connections, stop servers, conclude tests
|
|---|
| 205 | });
|
|---|
| 206 | ```
|
|---|
| 207 |
|
|---|
| 208 | - If the handler returns a value, the value is ignored
|
|---|
| 209 | - If the handler throws an error, the error passes to ``outputPromise``
|
|---|
| 210 | - If the handler returns a promise, ``outputPromise`` gets postponed. The
|
|---|
| 211 | eventual value or error has the same effect as an immediate return
|
|---|
| 212 | value or thrown error: a value would be ignored, an error would be
|
|---|
| 213 | forwarded.
|
|---|
| 214 |
|
|---|
| 215 | If you are writing JavaScript for modern engines only or using
|
|---|
| 216 | CoffeeScript, you may use `finally` instead of `fin`.
|
|---|
| 217 |
|
|---|
| 218 | ### Chaining
|
|---|
| 219 |
|
|---|
| 220 | There are two ways to chain promises. You can chain promises either
|
|---|
| 221 | inside or outside handlers. The next two examples are equivalent.
|
|---|
| 222 |
|
|---|
| 223 | ```javascript
|
|---|
| 224 | return getUsername()
|
|---|
| 225 | .then(function (username) {
|
|---|
| 226 | return getUser(username)
|
|---|
| 227 | .then(function (user) {
|
|---|
| 228 | // if we get here without an error,
|
|---|
| 229 | // the value returned here
|
|---|
| 230 | // or the exception thrown here
|
|---|
| 231 | // resolves the promise returned
|
|---|
| 232 | // by the first line
|
|---|
| 233 | })
|
|---|
| 234 | });
|
|---|
| 235 | ```
|
|---|
| 236 |
|
|---|
| 237 | ```javascript
|
|---|
| 238 | return getUsername()
|
|---|
| 239 | .then(function (username) {
|
|---|
| 240 | return getUser(username);
|
|---|
| 241 | })
|
|---|
| 242 | .then(function (user) {
|
|---|
| 243 | // if we get here without an error,
|
|---|
| 244 | // the value returned here
|
|---|
| 245 | // or the exception thrown here
|
|---|
| 246 | // resolves the promise returned
|
|---|
| 247 | // by the first line
|
|---|
| 248 | });
|
|---|
| 249 | ```
|
|---|
| 250 |
|
|---|
| 251 | The only difference is nesting. It’s useful to nest handlers if you
|
|---|
| 252 | need to capture multiple input values in your closure.
|
|---|
| 253 |
|
|---|
| 254 | ```javascript
|
|---|
| 255 | function authenticate() {
|
|---|
| 256 | return getUsername()
|
|---|
| 257 | .then(function (username) {
|
|---|
| 258 | return getUser(username);
|
|---|
| 259 | })
|
|---|
| 260 | // chained because we will not need the user name in the next event
|
|---|
| 261 | .then(function (user) {
|
|---|
| 262 | return getPassword()
|
|---|
| 263 | // nested because we need both user and password next
|
|---|
| 264 | .then(function (password) {
|
|---|
| 265 | if (user.passwordHash !== hash(password)) {
|
|---|
| 266 | throw new Error("Can't authenticate");
|
|---|
| 267 | }
|
|---|
| 268 | });
|
|---|
| 269 | });
|
|---|
| 270 | }
|
|---|
| 271 | ```
|
|---|
| 272 |
|
|---|
| 273 |
|
|---|
| 274 | ### Combination
|
|---|
| 275 |
|
|---|
| 276 | You can turn an array of promises into a promise for the whole,
|
|---|
| 277 | fulfilled array using ``all``.
|
|---|
| 278 |
|
|---|
| 279 | ```javascript
|
|---|
| 280 | return Q.all([
|
|---|
| 281 | eventualAdd(2, 2),
|
|---|
| 282 | eventualAdd(10, 20)
|
|---|
| 283 | ]);
|
|---|
| 284 | ```
|
|---|
| 285 |
|
|---|
| 286 | If you have a promise for an array, you can use ``spread`` as a
|
|---|
| 287 | replacement for ``then``. The ``spread`` function “spreads” the
|
|---|
| 288 | values over the arguments of the fulfillment handler. The rejection handler
|
|---|
| 289 | will get called at the first sign of failure. That is, whichever of
|
|---|
| 290 | the received promises fails first gets handled by the rejection handler.
|
|---|
| 291 |
|
|---|
| 292 | ```javascript
|
|---|
| 293 | function eventualAdd(a, b) {
|
|---|
| 294 | return Q.spread([a, b], function (a, b) {
|
|---|
| 295 | return a + b;
|
|---|
| 296 | })
|
|---|
| 297 | }
|
|---|
| 298 | ```
|
|---|
| 299 |
|
|---|
| 300 | But ``spread`` calls ``all`` initially, so you can skip it in chains.
|
|---|
| 301 |
|
|---|
| 302 | ```javascript
|
|---|
| 303 | return getUsername()
|
|---|
| 304 | .then(function (username) {
|
|---|
| 305 | return [username, getUser(username)];
|
|---|
| 306 | })
|
|---|
| 307 | .spread(function (username, user) {
|
|---|
| 308 | });
|
|---|
| 309 | ```
|
|---|
| 310 |
|
|---|
| 311 | The ``all`` function returns a promise for an array of values. When this
|
|---|
| 312 | promise is fulfilled, the array contains the fulfillment values of the original
|
|---|
| 313 | promises, in the same order as those promises. If one of the given promises
|
|---|
| 314 | is rejected, the returned promise is immediately rejected, not waiting for the
|
|---|
| 315 | rest of the batch. If you want to wait for all of the promises to either be
|
|---|
| 316 | fulfilled or rejected, you can use ``allSettled``.
|
|---|
| 317 |
|
|---|
| 318 | ```javascript
|
|---|
| 319 | Q.allSettled(promises)
|
|---|
| 320 | .then(function (results) {
|
|---|
| 321 | results.forEach(function (result) {
|
|---|
| 322 | if (result.state === "fulfilled") {
|
|---|
| 323 | var value = result.value;
|
|---|
| 324 | } else {
|
|---|
| 325 | var reason = result.reason;
|
|---|
| 326 | }
|
|---|
| 327 | });
|
|---|
| 328 | });
|
|---|
| 329 | ```
|
|---|
| 330 |
|
|---|
| 331 | The ``any`` function accepts an array of promises and returns a promise that is
|
|---|
| 332 | fulfilled by the first given promise to be fulfilled, or rejected if all of the
|
|---|
| 333 | given promises are rejected.
|
|---|
| 334 |
|
|---|
| 335 | ```javascript
|
|---|
| 336 | Q.any(promises)
|
|---|
| 337 | .then(function (first) {
|
|---|
| 338 | // Any of the promises was fulfilled.
|
|---|
| 339 | }, function (error) {
|
|---|
| 340 | // All of the promises were rejected.
|
|---|
| 341 | });
|
|---|
| 342 | ```
|
|---|
| 343 |
|
|---|
| 344 | ### Sequences
|
|---|
| 345 |
|
|---|
| 346 | If you have a number of promise-producing functions that need
|
|---|
| 347 | to be run sequentially, you can of course do so manually:
|
|---|
| 348 |
|
|---|
| 349 | ```javascript
|
|---|
| 350 | return foo(initialVal).then(bar).then(baz).then(qux);
|
|---|
| 351 | ```
|
|---|
| 352 |
|
|---|
| 353 | However, if you want to run a dynamically constructed sequence of
|
|---|
| 354 | functions, you'll want something like this:
|
|---|
| 355 |
|
|---|
| 356 | ```javascript
|
|---|
| 357 | var funcs = [foo, bar, baz, qux];
|
|---|
| 358 |
|
|---|
| 359 | var result = Q(initialVal);
|
|---|
| 360 | funcs.forEach(function (f) {
|
|---|
| 361 | result = result.then(f);
|
|---|
| 362 | });
|
|---|
| 363 | return result;
|
|---|
| 364 | ```
|
|---|
| 365 |
|
|---|
| 366 | You can make this slightly more compact using `reduce`:
|
|---|
| 367 |
|
|---|
| 368 | ```javascript
|
|---|
| 369 | return funcs.reduce(function (soFar, f) {
|
|---|
| 370 | return soFar.then(f);
|
|---|
| 371 | }, Q(initialVal));
|
|---|
| 372 | ```
|
|---|
| 373 |
|
|---|
| 374 | Or, you could use the ultra-compact version:
|
|---|
| 375 |
|
|---|
| 376 | ```javascript
|
|---|
| 377 | return funcs.reduce(Q.when, Q(initialVal));
|
|---|
| 378 | ```
|
|---|
| 379 |
|
|---|
| 380 | ### Handling Errors
|
|---|
| 381 |
|
|---|
| 382 | One sometimes-unintuitive aspect of promises is that if you throw an
|
|---|
| 383 | exception in the fulfillment handler, it will not be caught by the error
|
|---|
| 384 | handler.
|
|---|
| 385 |
|
|---|
| 386 | ```javascript
|
|---|
| 387 | return foo()
|
|---|
| 388 | .then(function (value) {
|
|---|
| 389 | throw new Error("Can't bar.");
|
|---|
| 390 | }, function (error) {
|
|---|
| 391 | // We only get here if "foo" fails
|
|---|
| 392 | });
|
|---|
| 393 | ```
|
|---|
| 394 |
|
|---|
| 395 | To see why this is, consider the parallel between promises and
|
|---|
| 396 | ``try``/``catch``. We are ``try``-ing to execute ``foo()``: the error
|
|---|
| 397 | handler represents a ``catch`` for ``foo()``, while the fulfillment handler
|
|---|
| 398 | represents code that happens *after* the ``try``/``catch`` block.
|
|---|
| 399 | That code then needs its own ``try``/``catch`` block.
|
|---|
| 400 |
|
|---|
| 401 | In terms of promises, this means chaining your rejection handler:
|
|---|
| 402 |
|
|---|
| 403 | ```javascript
|
|---|
| 404 | return foo()
|
|---|
| 405 | .then(function (value) {
|
|---|
| 406 | throw new Error("Can't bar.");
|
|---|
| 407 | })
|
|---|
| 408 | .fail(function (error) {
|
|---|
| 409 | // We get here with either foo's error or bar's error
|
|---|
| 410 | });
|
|---|
| 411 | ```
|
|---|
| 412 |
|
|---|
| 413 | ### Progress Notification
|
|---|
| 414 |
|
|---|
| 415 | It's possible for promises to report their progress, e.g. for tasks that take a
|
|---|
| 416 | long time like a file upload. Not all promises will implement progress
|
|---|
| 417 | notifications, but for those that do, you can consume the progress values using
|
|---|
| 418 | a third parameter to ``then``:
|
|---|
| 419 |
|
|---|
| 420 | ```javascript
|
|---|
| 421 | return uploadFile()
|
|---|
| 422 | .then(function () {
|
|---|
| 423 | // Success uploading the file
|
|---|
| 424 | }, function (err) {
|
|---|
| 425 | // There was an error, and we get the reason for error
|
|---|
| 426 | }, function (progress) {
|
|---|
| 427 | // We get notified of the upload's progress as it is executed
|
|---|
| 428 | });
|
|---|
| 429 | ```
|
|---|
| 430 |
|
|---|
| 431 | Like `fail`, Q also provides a shorthand for progress callbacks
|
|---|
| 432 | called `progress`:
|
|---|
| 433 |
|
|---|
| 434 | ```javascript
|
|---|
| 435 | return uploadFile().progress(function (progress) {
|
|---|
| 436 | // We get notified of the upload's progress
|
|---|
| 437 | });
|
|---|
| 438 | ```
|
|---|
| 439 |
|
|---|
| 440 | ### The End
|
|---|
| 441 |
|
|---|
| 442 | When you get to the end of a chain of promises, you should either
|
|---|
| 443 | return the last promise or end the chain. Since handlers catch
|
|---|
| 444 | errors, it’s an unfortunate pattern that the exceptions can go
|
|---|
| 445 | unobserved.
|
|---|
| 446 |
|
|---|
| 447 | So, either return it,
|
|---|
| 448 |
|
|---|
| 449 | ```javascript
|
|---|
| 450 | return foo()
|
|---|
| 451 | .then(function () {
|
|---|
| 452 | return "bar";
|
|---|
| 453 | });
|
|---|
| 454 | ```
|
|---|
| 455 |
|
|---|
| 456 | Or, end it.
|
|---|
| 457 |
|
|---|
| 458 | ```javascript
|
|---|
| 459 | foo()
|
|---|
| 460 | .then(function () {
|
|---|
| 461 | return "bar";
|
|---|
| 462 | })
|
|---|
| 463 | .done();
|
|---|
| 464 | ```
|
|---|
| 465 |
|
|---|
| 466 | Ending a promise chain makes sure that, if an error doesn’t get
|
|---|
| 467 | handled before the end, it will get rethrown and reported.
|
|---|
| 468 |
|
|---|
| 469 | This is a stopgap. We are exploring ways to make unhandled errors
|
|---|
| 470 | visible without any explicit handling.
|
|---|
| 471 |
|
|---|
| 472 |
|
|---|
| 473 | ### The Beginning
|
|---|
| 474 |
|
|---|
| 475 | Everything above assumes you get a promise from somewhere else. This
|
|---|
| 476 | is the common case. Every once in a while, you will need to create a
|
|---|
| 477 | promise from scratch.
|
|---|
| 478 |
|
|---|
| 479 | #### Using ``Q.fcall``
|
|---|
| 480 |
|
|---|
| 481 | You can create a promise from a value using ``Q.fcall``. This returns a
|
|---|
| 482 | promise for 10.
|
|---|
| 483 |
|
|---|
| 484 | ```javascript
|
|---|
| 485 | return Q.fcall(function () {
|
|---|
| 486 | return 10;
|
|---|
| 487 | });
|
|---|
| 488 | ```
|
|---|
| 489 |
|
|---|
| 490 | You can also use ``fcall`` to get a promise for an exception.
|
|---|
| 491 |
|
|---|
| 492 | ```javascript
|
|---|
| 493 | return Q.fcall(function () {
|
|---|
| 494 | throw new Error("Can't do it");
|
|---|
| 495 | });
|
|---|
| 496 | ```
|
|---|
| 497 |
|
|---|
| 498 | As the name implies, ``fcall`` can call functions, or even promised
|
|---|
| 499 | functions. This uses the ``eventualAdd`` function above to add two
|
|---|
| 500 | numbers.
|
|---|
| 501 |
|
|---|
| 502 | ```javascript
|
|---|
| 503 | return Q.fcall(eventualAdd, 2, 2);
|
|---|
| 504 | ```
|
|---|
| 505 |
|
|---|
| 506 |
|
|---|
| 507 | #### Using Deferreds
|
|---|
| 508 |
|
|---|
| 509 | If you have to interface with asynchronous functions that are callback-based
|
|---|
| 510 | instead of promise-based, Q provides a few shortcuts (like ``Q.nfcall`` and
|
|---|
| 511 | friends). But much of the time, the solution will be to use *deferreds*.
|
|---|
| 512 |
|
|---|
| 513 | ```javascript
|
|---|
| 514 | var deferred = Q.defer();
|
|---|
| 515 | FS.readFile("foo.txt", "utf-8", function (error, text) {
|
|---|
| 516 | if (error) {
|
|---|
| 517 | deferred.reject(new Error(error));
|
|---|
| 518 | } else {
|
|---|
| 519 | deferred.resolve(text);
|
|---|
| 520 | }
|
|---|
| 521 | });
|
|---|
| 522 | return deferred.promise;
|
|---|
| 523 | ```
|
|---|
| 524 |
|
|---|
| 525 | Note that a deferred can be resolved with a value or a promise. The
|
|---|
| 526 | ``reject`` function is a shorthand for resolving with a rejected
|
|---|
| 527 | promise.
|
|---|
| 528 |
|
|---|
| 529 | ```javascript
|
|---|
| 530 | // this:
|
|---|
| 531 | deferred.reject(new Error("Can't do it"));
|
|---|
| 532 |
|
|---|
| 533 | // is shorthand for:
|
|---|
| 534 | var rejection = Q.fcall(function () {
|
|---|
| 535 | throw new Error("Can't do it");
|
|---|
| 536 | });
|
|---|
| 537 | deferred.resolve(rejection);
|
|---|
| 538 | ```
|
|---|
| 539 |
|
|---|
| 540 | This is a simplified implementation of ``Q.delay``.
|
|---|
| 541 |
|
|---|
| 542 | ```javascript
|
|---|
| 543 | function delay(ms) {
|
|---|
| 544 | var deferred = Q.defer();
|
|---|
| 545 | setTimeout(deferred.resolve, ms);
|
|---|
| 546 | return deferred.promise;
|
|---|
| 547 | }
|
|---|
| 548 | ```
|
|---|
| 549 |
|
|---|
| 550 | This is a simplified implementation of ``Q.timeout``
|
|---|
| 551 |
|
|---|
| 552 | ```javascript
|
|---|
| 553 | function timeout(promise, ms) {
|
|---|
| 554 | var deferred = Q.defer();
|
|---|
| 555 | Q.when(promise, deferred.resolve);
|
|---|
| 556 | delay(ms).then(function () {
|
|---|
| 557 | deferred.reject(new Error("Timed out"));
|
|---|
| 558 | });
|
|---|
| 559 | return deferred.promise;
|
|---|
| 560 | }
|
|---|
| 561 | ```
|
|---|
| 562 |
|
|---|
| 563 | Finally, you can send a progress notification to the promise with
|
|---|
| 564 | ``deferred.notify``.
|
|---|
| 565 |
|
|---|
| 566 | For illustration, this is a wrapper for XML HTTP requests in the browser. Note
|
|---|
| 567 | that a more [thorough][XHR] implementation would be in order in practice.
|
|---|
| 568 |
|
|---|
| 569 | [XHR]: https://github.com/montagejs/mr/blob/71e8df99bb4f0584985accd6f2801ef3015b9763/browser.js#L29-L73
|
|---|
| 570 |
|
|---|
| 571 | ```javascript
|
|---|
| 572 | function requestOkText(url) {
|
|---|
| 573 | var request = new XMLHttpRequest();
|
|---|
| 574 | var deferred = Q.defer();
|
|---|
| 575 |
|
|---|
| 576 | request.open("GET", url, true);
|
|---|
| 577 | request.onload = onload;
|
|---|
| 578 | request.onerror = onerror;
|
|---|
| 579 | request.onprogress = onprogress;
|
|---|
| 580 | request.send();
|
|---|
| 581 |
|
|---|
| 582 | function onload() {
|
|---|
| 583 | if (request.status === 200) {
|
|---|
| 584 | deferred.resolve(request.responseText);
|
|---|
| 585 | } else {
|
|---|
| 586 | deferred.reject(new Error("Status code was " + request.status));
|
|---|
| 587 | }
|
|---|
| 588 | }
|
|---|
| 589 |
|
|---|
| 590 | function onerror() {
|
|---|
| 591 | deferred.reject(new Error("Can't XHR " + JSON.stringify(url)));
|
|---|
| 592 | }
|
|---|
| 593 |
|
|---|
| 594 | function onprogress(event) {
|
|---|
| 595 | deferred.notify(event.loaded / event.total);
|
|---|
| 596 | }
|
|---|
| 597 |
|
|---|
| 598 | return deferred.promise;
|
|---|
| 599 | }
|
|---|
| 600 | ```
|
|---|
| 601 |
|
|---|
| 602 | Below is an example of how to use this ``requestOkText`` function:
|
|---|
| 603 |
|
|---|
| 604 | ```javascript
|
|---|
| 605 | requestOkText("http://localhost:3000")
|
|---|
| 606 | .then(function (responseText) {
|
|---|
| 607 | // If the HTTP response returns 200 OK, log the response text.
|
|---|
| 608 | console.log(responseText);
|
|---|
| 609 | }, function (error) {
|
|---|
| 610 | // If there's an error or a non-200 status code, log the error.
|
|---|
| 611 | console.error(error);
|
|---|
| 612 | }, function (progress) {
|
|---|
| 613 | // Log the progress as it comes in.
|
|---|
| 614 | console.log("Request progress: " + Math.round(progress * 100) + "%");
|
|---|
| 615 | });
|
|---|
| 616 | ```
|
|---|
| 617 |
|
|---|
| 618 | #### Using `Q.Promise`
|
|---|
| 619 |
|
|---|
| 620 | This is an alternative promise-creation API that has the same power as
|
|---|
| 621 | the deferred concept, but without introducing another conceptual entity.
|
|---|
| 622 |
|
|---|
| 623 | Rewriting the `requestOkText` example above using `Q.Promise`:
|
|---|
| 624 |
|
|---|
| 625 | ```javascript
|
|---|
| 626 | function requestOkText(url) {
|
|---|
| 627 | return Q.Promise(function(resolve, reject, notify) {
|
|---|
| 628 | var request = new XMLHttpRequest();
|
|---|
| 629 |
|
|---|
| 630 | request.open("GET", url, true);
|
|---|
| 631 | request.onload = onload;
|
|---|
| 632 | request.onerror = onerror;
|
|---|
| 633 | request.onprogress = onprogress;
|
|---|
| 634 | request.send();
|
|---|
| 635 |
|
|---|
| 636 | function onload() {
|
|---|
| 637 | if (request.status === 200) {
|
|---|
| 638 | resolve(request.responseText);
|
|---|
| 639 | } else {
|
|---|
| 640 | reject(new Error("Status code was " + request.status));
|
|---|
| 641 | }
|
|---|
| 642 | }
|
|---|
| 643 |
|
|---|
| 644 | function onerror() {
|
|---|
| 645 | reject(new Error("Can't XHR " + JSON.stringify(url)));
|
|---|
| 646 | }
|
|---|
| 647 |
|
|---|
| 648 | function onprogress(event) {
|
|---|
| 649 | notify(event.loaded / event.total);
|
|---|
| 650 | }
|
|---|
| 651 | });
|
|---|
| 652 | }
|
|---|
| 653 | ```
|
|---|
| 654 |
|
|---|
| 655 | If `requestOkText` were to throw an exception, the returned promise would be
|
|---|
| 656 | rejected with that thrown exception as the rejection reason.
|
|---|
| 657 |
|
|---|
| 658 | ### The Middle
|
|---|
| 659 |
|
|---|
| 660 | If you are using a function that may return a promise, but just might
|
|---|
| 661 | return a value if it doesn’t need to defer, you can use the “static”
|
|---|
| 662 | methods of the Q library.
|
|---|
| 663 |
|
|---|
| 664 | The ``when`` function is the static equivalent for ``then``.
|
|---|
| 665 |
|
|---|
| 666 | ```javascript
|
|---|
| 667 | return Q.when(valueOrPromise, function (value) {
|
|---|
| 668 | }, function (error) {
|
|---|
| 669 | });
|
|---|
| 670 | ```
|
|---|
| 671 |
|
|---|
| 672 | All of the other methods on a promise have static analogs with the
|
|---|
| 673 | same name.
|
|---|
| 674 |
|
|---|
| 675 | The following are equivalent:
|
|---|
| 676 |
|
|---|
| 677 | ```javascript
|
|---|
| 678 | return Q.all([a, b]);
|
|---|
| 679 | ```
|
|---|
| 680 |
|
|---|
| 681 | ```javascript
|
|---|
| 682 | return Q.fcall(function () {
|
|---|
| 683 | return [a, b];
|
|---|
| 684 | })
|
|---|
| 685 | .all();
|
|---|
| 686 | ```
|
|---|
| 687 |
|
|---|
| 688 | When working with promises provided by other libraries, you should
|
|---|
| 689 | convert it to a Q promise. Not all promise libraries make the same
|
|---|
| 690 | guarantees as Q and certainly don’t provide all of the same methods.
|
|---|
| 691 | Most libraries only provide a partially functional ``then`` method.
|
|---|
| 692 | This thankfully is all we need to turn them into vibrant Q promises.
|
|---|
| 693 |
|
|---|
| 694 | ```javascript
|
|---|
| 695 | return Q($.ajax(...))
|
|---|
| 696 | .then(function () {
|
|---|
| 697 | });
|
|---|
| 698 | ```
|
|---|
| 699 |
|
|---|
| 700 | If there is any chance that the promise you receive is not a Q promise
|
|---|
| 701 | as provided by your library, you should wrap it using a Q function.
|
|---|
| 702 | You can even use ``Q.invoke`` as a shorthand.
|
|---|
| 703 |
|
|---|
| 704 | ```javascript
|
|---|
| 705 | return Q.invoke($, 'ajax', ...)
|
|---|
| 706 | .then(function () {
|
|---|
| 707 | });
|
|---|
| 708 | ```
|
|---|
| 709 |
|
|---|
| 710 |
|
|---|
| 711 | ### Over the Wire
|
|---|
| 712 |
|
|---|
| 713 | A promise can serve as a proxy for another object, even a remote
|
|---|
| 714 | object. There are methods that allow you to optimistically manipulate
|
|---|
| 715 | properties or call functions. All of these interactions return
|
|---|
| 716 | promises, so they can be chained.
|
|---|
| 717 |
|
|---|
| 718 | ```
|
|---|
| 719 | direct manipulation using a promise as a proxy
|
|---|
| 720 | -------------------------- -------------------------------
|
|---|
| 721 | value.foo promise.get("foo")
|
|---|
| 722 | value.foo = value promise.put("foo", value)
|
|---|
| 723 | delete value.foo promise.del("foo")
|
|---|
| 724 | value.foo(...args) promise.post("foo", [args])
|
|---|
| 725 | value.foo(...args) promise.invoke("foo", ...args)
|
|---|
| 726 | value(...args) promise.fapply([args])
|
|---|
| 727 | value(...args) promise.fcall(...args)
|
|---|
| 728 | ```
|
|---|
| 729 |
|
|---|
| 730 | If the promise is a proxy for a remote object, you can shave
|
|---|
| 731 | round-trips by using these functions instead of ``then``. To take
|
|---|
| 732 | advantage of promises for remote objects, check out [Q-Connection][].
|
|---|
| 733 |
|
|---|
| 734 | [Q-Connection]: https://github.com/kriskowal/q-connection
|
|---|
| 735 |
|
|---|
| 736 | Even in the case of non-remote objects, these methods can be used as
|
|---|
| 737 | shorthand for particularly-simple fulfillment handlers. For example, you
|
|---|
| 738 | can replace
|
|---|
| 739 |
|
|---|
| 740 | ```javascript
|
|---|
| 741 | return Q.fcall(function () {
|
|---|
| 742 | return [{ foo: "bar" }, { foo: "baz" }];
|
|---|
| 743 | })
|
|---|
| 744 | .then(function (value) {
|
|---|
| 745 | return value[0].foo;
|
|---|
| 746 | });
|
|---|
| 747 | ```
|
|---|
| 748 |
|
|---|
| 749 | with
|
|---|
| 750 |
|
|---|
| 751 | ```javascript
|
|---|
| 752 | return Q.fcall(function () {
|
|---|
| 753 | return [{ foo: "bar" }, { foo: "baz" }];
|
|---|
| 754 | })
|
|---|
| 755 | .get(0)
|
|---|
| 756 | .get("foo");
|
|---|
| 757 | ```
|
|---|
| 758 |
|
|---|
| 759 |
|
|---|
| 760 | ### Adapting Node
|
|---|
| 761 |
|
|---|
| 762 | If you're working with functions that make use of the Node.js callback pattern,
|
|---|
| 763 | where callbacks are in the form of `function(err, result)`, Q provides a few
|
|---|
| 764 | useful utility functions for converting between them. The most straightforward
|
|---|
| 765 | are probably `Q.nfcall` and `Q.nfapply` ("Node function call/apply") for calling
|
|---|
| 766 | Node.js-style functions and getting back a promise:
|
|---|
| 767 |
|
|---|
| 768 | ```javascript
|
|---|
| 769 | return Q.nfcall(FS.readFile, "foo.txt", "utf-8");
|
|---|
| 770 | return Q.nfapply(FS.readFile, ["foo.txt", "utf-8"]);
|
|---|
| 771 | ```
|
|---|
| 772 |
|
|---|
| 773 | If you are working with methods, instead of simple functions, you can easily
|
|---|
| 774 | run in to the usual problems where passing a method to another function—like
|
|---|
| 775 | `Q.nfcall`—"un-binds" the method from its owner. To avoid this, you can either
|
|---|
| 776 | use `Function.prototype.bind` or some nice shortcut methods we provide:
|
|---|
| 777 |
|
|---|
| 778 | ```javascript
|
|---|
| 779 | return Q.ninvoke(redisClient, "get", "user:1:id");
|
|---|
| 780 | return Q.npost(redisClient, "get", ["user:1:id"]);
|
|---|
| 781 | ```
|
|---|
| 782 |
|
|---|
| 783 | You can also create reusable wrappers with `Q.denodeify` or `Q.nbind`:
|
|---|
| 784 |
|
|---|
| 785 | ```javascript
|
|---|
| 786 | var readFile = Q.denodeify(FS.readFile);
|
|---|
| 787 | return readFile("foo.txt", "utf-8");
|
|---|
| 788 |
|
|---|
| 789 | var redisClientGet = Q.nbind(redisClient.get, redisClient);
|
|---|
| 790 | return redisClientGet("user:1:id");
|
|---|
| 791 | ```
|
|---|
| 792 |
|
|---|
| 793 | Finally, if you're working with raw deferred objects, there is a
|
|---|
| 794 | `makeNodeResolver` method on deferreds that can be handy:
|
|---|
| 795 |
|
|---|
| 796 | ```javascript
|
|---|
| 797 | var deferred = Q.defer();
|
|---|
| 798 | FS.readFile("foo.txt", "utf-8", deferred.makeNodeResolver());
|
|---|
| 799 | return deferred.promise;
|
|---|
| 800 | ```
|
|---|
| 801 |
|
|---|
| 802 | ### Long Stack Traces
|
|---|
| 803 |
|
|---|
| 804 | Q comes with optional support for “long stack traces,” wherein the `stack`
|
|---|
| 805 | property of `Error` rejection reasons is rewritten to be traced along
|
|---|
| 806 | asynchronous jumps instead of stopping at the most recent one. As an example:
|
|---|
| 807 |
|
|---|
| 808 | ```js
|
|---|
| 809 | function theDepthsOfMyProgram() {
|
|---|
| 810 | Q.delay(100).done(function explode() {
|
|---|
| 811 | throw new Error("boo!");
|
|---|
| 812 | });
|
|---|
| 813 | }
|
|---|
| 814 |
|
|---|
| 815 | theDepthsOfMyProgram();
|
|---|
| 816 | ```
|
|---|
| 817 |
|
|---|
| 818 | usually would give a rather unhelpful stack trace looking something like
|
|---|
| 819 |
|
|---|
| 820 | ```
|
|---|
| 821 | Error: boo!
|
|---|
| 822 | at explode (/path/to/test.js:3:11)
|
|---|
| 823 | at _fulfilled (/path/to/test.js:q:54)
|
|---|
| 824 | at resolvedValue.promiseDispatch.done (/path/to/q.js:823:30)
|
|---|
| 825 | at makePromise.promise.promiseDispatch (/path/to/q.js:496:13)
|
|---|
| 826 | at pending (/path/to/q.js:397:39)
|
|---|
| 827 | at process.startup.processNextTick.process._tickCallback (node.js:244:9)
|
|---|
| 828 | ```
|
|---|
| 829 |
|
|---|
| 830 | But, if you turn this feature on by setting
|
|---|
| 831 |
|
|---|
| 832 | ```js
|
|---|
| 833 | Q.longStackSupport = true;
|
|---|
| 834 | ```
|
|---|
| 835 |
|
|---|
| 836 | then the above code gives a nice stack trace to the tune of
|
|---|
| 837 |
|
|---|
| 838 | ```
|
|---|
| 839 | Error: boo!
|
|---|
| 840 | at explode (/path/to/test.js:3:11)
|
|---|
| 841 | From previous event:
|
|---|
| 842 | at theDepthsOfMyProgram (/path/to/test.js:2:16)
|
|---|
| 843 | at Object.<anonymous> (/path/to/test.js:7:1)
|
|---|
| 844 | ```
|
|---|
| 845 |
|
|---|
| 846 | Note how you can see the function that triggered the async operation in the
|
|---|
| 847 | stack trace! This is very helpful for debugging, as otherwise you end up getting
|
|---|
| 848 | only the first line, plus a bunch of Q internals, with no sign of where the
|
|---|
| 849 | operation started.
|
|---|
| 850 |
|
|---|
| 851 | In node.js, this feature can also be enabled through the Q_DEBUG environment
|
|---|
| 852 | variable:
|
|---|
| 853 |
|
|---|
| 854 | ```
|
|---|
| 855 | Q_DEBUG=1 node server.js
|
|---|
| 856 | ```
|
|---|
| 857 |
|
|---|
| 858 | This will enable long stack support in every instance of Q.
|
|---|
| 859 |
|
|---|
| 860 | This feature does come with somewhat-serious performance and memory overhead,
|
|---|
| 861 | however. If you're working with lots of promises, or trying to scale a server
|
|---|
| 862 | to many users, you should probably keep it off. But in development, go for it!
|
|---|
| 863 |
|
|---|
| 864 | ## Tests
|
|---|
| 865 |
|
|---|
| 866 | You can view the results of the Q test suite [in your browser][tests]!
|
|---|
| 867 |
|
|---|
| 868 | [tests]: https://rawgithub.com/kriskowal/q/v1/spec/q-spec.html
|
|---|
| 869 |
|
|---|
| 870 | ## License
|
|---|
| 871 |
|
|---|
| 872 | Copyright 2009–2017 Kristopher Michael Kowal and contributors
|
|---|
| 873 | MIT License (enclosed)
|
|---|
| 874 |
|
|---|