UNPKG

25.4 kBMarkdownView Raw
1[![Build Status](https://secure.travis-ci.org/kriskowal/q.svg?branch=master)](http://travis-ci.org/kriskowal/q)
2[![CDNJS](https://img.shields.io/cdnjs/v/q.js.svg)](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
8If a function cannot return a value or throw an exception without
9blocking, it can return a promise instead. A promise is an object
10that represents the return value or the thrown exception that the
11function may eventually provide. A promise can also be used as a
12proxy for a [remote object][Q-Connection] to overcome latency.
13
14[Q-Connection]: https://github.com/kriskowal/q-connection
15
16On the first pass, promises can mitigate the “[Pyramid of
17Doom][POD]”: the situation where code marches to the right faster
18than it marches forward.
19
20[POD]: http://calculist.org/blog/2011/12/14/why-coroutines-wont-work-on-the-web/
21
22```javascript
23step1(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
34With a promise library, you can flatten the pyramid.
35
36```javascript
37Q.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
50With 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
52the `catch` function, where it’s caught and handled. (Here `promisedStepN` is
53a version of `stepN` that returns a promise.)
54
55The callback approach is called an “inversion of control”.
56A function that accepts a callback instead of a return value
57is saying, “Don’t call me, I’ll call you.”. Promises
58[un-invert][IOC] the inversion, cleanly separating the input
59arguments from control flow arguments. This simplifies the
60use and creation of API’s, particularly variadic,
61rest 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
68The 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
79Q can exchange promises with jQuery, Dojo, When.js, WinJS, and more.
80
81## Resources
82
83Our [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
97We'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
110Promises have a ``then`` method, which you can use to get the eventual
111return value (fulfillment) or thrown exception (rejection).
112
113```javascript
114promiseMeSomething()
115.then(function (value) {
116}, function (reason) {
117});
118```
119
120If ``promiseMeSomething`` returns a promise that gets fulfilled later
121with a return value, the first function (the fulfillment handler) will be
122called with the value. However, if the ``promiseMeSomething`` function
123gets rejected later by a thrown exception, the second function (the
124rejection handler) will be called with the exception.
125
126Note that resolution of a promise is always asynchronous: that is, the
127fulfillment or rejection handler will always be called in the next turn of the
128event loop (i.e. `process.nextTick` in Node). This gives you a nice
129guarantee when mentally tracing the flow of your code, namely that
130``then`` will always return before either handler is executed.
131
132In this tutorial, we begin with how to consume and work with promises. We'll
133talk about how to create them, and thus create functions like
134`promiseMeSomething` that return promises, [below](#the-beginning).
135
136
137### Propagation
138
139The ``then`` method returns a promise, which in this example, I’m
140assigning to ``outputPromise``.
141
142```javascript
143var outputPromise = getInputPromise()
144.then(function (input) {
145}, function (reason) {
146});
147```
148
149The ``outputPromise`` variable becomes a new promise for the return
150value of either handler. Since a function can only either return a
151value or throw an exception, only one handler will ever be called and it
152will 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
164If the ``getInputPromise()`` promise gets rejected and you omit the
165rejection handler, the **error** will go to ``outputPromise``:
166
167```javascript
168var outputPromise = getInputPromise()
169.then(function (value) {
170});
171```
172
173If the input promise gets fulfilled and you omit the fulfillment handler, the
174**value** will go to ``outputPromise``:
175
176```javascript
177var outputPromise = getInputPromise()
178.then(null, function (error) {
179});
180```
181
182Q promises provide a ``fail`` shorthand for ``then`` when you are only
183interested in handling the error:
184
185```javascript
186var outputPromise = getInputPromise()
187.fail(function (error) {
188});
189```
190
191If you are writing JavaScript for modern engines only or using
192CoffeeScript, you may use `catch` instead of `fail`.
193
194Promises also have a ``fin`` function that is like a ``finally`` clause.
195The final handler gets called, with no arguments, when the promise
196returned by ``getInputPromise()`` either returns a value or throws an
197error. The value returned or error thrown by ``getInputPromise()``
198passes directly to ``outputPromise`` unless the final handler fails, and
199may be delayed if the final handler returns a promise.
200
201```javascript
202var 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
215If you are writing JavaScript for modern engines only or using
216CoffeeScript, you may use `finally` instead of `fin`.
217
218### Chaining
219
220There are two ways to chain promises. You can chain promises either
221inside or outside handlers. The next two examples are equivalent.
222
223```javascript
224return 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
238return 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
251The only difference is nesting. It’s useful to nest handlers if you
252need to capture multiple input values in your closure.
253
254```javascript
255function 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
276You can turn an array of promises into a promise for the whole,
277fulfilled array using ``all``.
278
279```javascript
280return Q.all([
281 eventualAdd(2, 2),
282 eventualAdd(10, 20)
283]);
284```
285
286If you have a promise for an array, you can use ``spread`` as a
287replacement for ``then``. The ``spread`` function “spreads” the
288values over the arguments of the fulfillment handler. The rejection handler
289will get called at the first sign of failure. That is, whichever of
290the received promises fails first gets handled by the rejection handler.
291
292```javascript
293function eventualAdd(a, b) {
294 return Q.spread([a, b], function (a, b) {
295 return a + b;
296 })
297}
298```
299
300But ``spread`` calls ``all`` initially, so you can skip it in chains.
301
302```javascript
303return getUsername()
304.then(function (username) {
305 return [username, getUser(username)];
306})
307.spread(function (username, user) {
308});
309```
310
311The ``all`` function returns a promise for an array of values. When this
312promise is fulfilled, the array contains the fulfillment values of the original
313promises, in the same order as those promises. If one of the given promises
314is rejected, the returned promise is immediately rejected, not waiting for the
315rest of the batch. If you want to wait for all of the promises to either be
316fulfilled or rejected, you can use ``allSettled``.
317
318```javascript
319Q.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
331The ``any`` function accepts an array of promises and returns a promise that is
332fulfilled by the first given promise to be fulfilled, or rejected if all of the
333given promises are rejected.
334
335```javascript
336Q.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
346If you have a number of promise-producing functions that need
347to be run sequentially, you can of course do so manually:
348
349```javascript
350return foo(initialVal).then(bar).then(baz).then(qux);
351```
352
353However, if you want to run a dynamically constructed sequence of
354functions, you'll want something like this:
355
356```javascript
357var funcs = [foo, bar, baz, qux];
358
359var result = Q(initialVal);
360funcs.forEach(function (f) {
361 result = result.then(f);
362});
363return result;
364```
365
366You can make this slightly more compact using `reduce`:
367
368```javascript
369return funcs.reduce(function (soFar, f) {
370 return soFar.then(f);
371}, Q(initialVal));
372```
373
374Or, you could use the ultra-compact version:
375
376```javascript
377return funcs.reduce(Q.when, Q(initialVal));
378```
379
380### Handling Errors
381
382One sometimes-unintuitive aspect of promises is that if you throw an
383exception in the fulfillment handler, it will not be caught by the error
384handler.
385
386```javascript
387return 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
395To see why this is, consider the parallel between promises and
396``try``/``catch``. We are ``try``-ing to execute ``foo()``: the error
397handler represents a ``catch`` for ``foo()``, while the fulfillment handler
398represents code that happens *after* the ``try``/``catch`` block.
399That code then needs its own ``try``/``catch`` block.
400
401In terms of promises, this means chaining your rejection handler:
402
403```javascript
404return 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
415It's possible for promises to report their progress, e.g. for tasks that take a
416long time like a file upload. Not all promises will implement progress
417notifications, but for those that do, you can consume the progress values using
418a third parameter to ``then``:
419
420```javascript
421return 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
431Like `fail`, Q also provides a shorthand for progress callbacks
432called `progress`:
433
434```javascript
435return uploadFile().progress(function (progress) {
436 // We get notified of the upload's progress
437});
438```
439
440### The End
441
442When you get to the end of a chain of promises, you should either
443return the last promise or end the chain. Since handlers catch
444errors, it’s an unfortunate pattern that the exceptions can go
445unobserved.
446
447So, either return it,
448
449```javascript
450return foo()
451.then(function () {
452 return "bar";
453});
454```
455
456Or, end it.
457
458```javascript
459foo()
460.then(function () {
461 return "bar";
462})
463.done();
464```
465
466Ending a promise chain makes sure that, if an error doesn’t get
467handled before the end, it will get rethrown and reported.
468
469This is a stopgap. We are exploring ways to make unhandled errors
470visible without any explicit handling.
471
472
473### The Beginning
474
475Everything above assumes you get a promise from somewhere else. This
476is the common case. Every once in a while, you will need to create a
477promise from scratch.
478
479#### Using ``Q.fcall``
480
481You can create a promise from a value using ``Q.fcall``. This returns a
482promise for 10.
483
484```javascript
485return Q.fcall(function () {
486 return 10;
487});
488```
489
490You can also use ``fcall`` to get a promise for an exception.
491
492```javascript
493return Q.fcall(function () {
494 throw new Error("Can't do it");
495});
496```
497
498As the name implies, ``fcall`` can call functions, or even promised
499functions. This uses the ``eventualAdd`` function above to add two
500numbers.
501
502```javascript
503return Q.fcall(eventualAdd, 2, 2);
504```
505
506
507#### Using Deferreds
508
509If you have to interface with asynchronous functions that are callback-based
510instead of promise-based, Q provides a few shortcuts (like ``Q.nfcall`` and
511friends). But much of the time, the solution will be to use *deferreds*.
512
513```javascript
514var deferred = Q.defer();
515FS.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});
522return deferred.promise;
523```
524
525Note that a deferred can be resolved with a value or a promise. The
526``reject`` function is a shorthand for resolving with a rejected
527promise.
528
529```javascript
530// this:
531deferred.reject(new Error("Can't do it"));
532
533// is shorthand for:
534var rejection = Q.fcall(function () {
535 throw new Error("Can't do it");
536});
537deferred.resolve(rejection);
538```
539
540This is a simplified implementation of ``Q.delay``.
541
542```javascript
543function delay(ms) {
544 var deferred = Q.defer();
545 setTimeout(deferred.resolve, ms);
546 return deferred.promise;
547}
548```
549
550This is a simplified implementation of ``Q.timeout``
551
552```javascript
553function 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
563Finally, you can send a progress notification to the promise with
564``deferred.notify``.
565
566For illustration, this is a wrapper for XML HTTP requests in the browser. Note
567that 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
572function 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
602Below is an example of how to use this ``requestOkText`` function:
603
604```javascript
605requestOkText("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
620This is an alternative promise-creation API that has the same power as
621the deferred concept, but without introducing another conceptual entity.
622
623Rewriting the `requestOkText` example above using `Q.Promise`:
624
625```javascript
626function 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
655If `requestOkText` were to throw an exception, the returned promise would be
656rejected with that thrown exception as the rejection reason.
657
658### The Middle
659
660If you are using a function that may return a promise, but just might
661return a value if it doesn’t need to defer, you can use the “static”
662methods of the Q library.
663
664The ``when`` function is the static equivalent for ``then``.
665
666```javascript
667return Q.when(valueOrPromise, function (value) {
668}, function (error) {
669});
670```
671
672All of the other methods on a promise have static analogs with the
673same name.
674
675The following are equivalent:
676
677```javascript
678return Q.all([a, b]);
679```
680
681```javascript
682return Q.fcall(function () {
683 return [a, b];
684})
685.all();
686```
687
688When working with promises provided by other libraries, you should
689convert it to a Q promise. Not all promise libraries make the same
690guarantees as Q and certainly don’t provide all of the same methods.
691Most libraries only provide a partially functional ``then`` method.
692This thankfully is all we need to turn them into vibrant Q promises.
693
694```javascript
695return Q($.ajax(...))
696.then(function () {
697});
698```
699
700If there is any chance that the promise you receive is not a Q promise
701as provided by your library, you should wrap it using a Q function.
702You can even use ``Q.invoke`` as a shorthand.
703
704```javascript
705return Q.invoke($, 'ajax', ...)
706.then(function () {
707});
708```
709
710
711### Over the Wire
712
713A promise can serve as a proxy for another object, even a remote
714object. There are methods that allow you to optimistically manipulate
715properties or call functions. All of these interactions return
716promises, so they can be chained.
717
718```
719direct manipulation using a promise as a proxy
720-------------------------- -------------------------------
721value.foo promise.get("foo")
722value.foo = value promise.put("foo", value)
723delete value.foo promise.del("foo")
724value.foo(...args) promise.post("foo", [args])
725value.foo(...args) promise.invoke("foo", ...args)
726value(...args) promise.fapply([args])
727value(...args) promise.fcall(...args)
728```
729
730If the promise is a proxy for a remote object, you can shave
731round-trips by using these functions instead of ``then``. To take
732advantage of promises for remote objects, check out [Q-Connection][].
733
734[Q-Connection]: https://github.com/kriskowal/q-connection
735
736Even in the case of non-remote objects, these methods can be used as
737shorthand for particularly-simple fulfillment handlers. For example, you
738can replace
739
740```javascript
741return Q.fcall(function () {
742 return [{ foo: "bar" }, { foo: "baz" }];
743})
744.then(function (value) {
745 return value[0].foo;
746});
747```
748
749with
750
751```javascript
752return Q.fcall(function () {
753 return [{ foo: "bar" }, { foo: "baz" }];
754})
755.get(0)
756.get("foo");
757```
758
759
760### Adapting Node
761
762If you're working with functions that make use of the Node.js callback pattern,
763where callbacks are in the form of `function(err, result)`, Q provides a few
764useful utility functions for converting between them. The most straightforward
765are probably `Q.nfcall` and `Q.nfapply` ("Node function call/apply") for calling
766Node.js-style functions and getting back a promise:
767
768```javascript
769return Q.nfcall(FS.readFile, "foo.txt", "utf-8");
770return Q.nfapply(FS.readFile, ["foo.txt", "utf-8"]);
771```
772
773If you are working with methods, instead of simple functions, you can easily
774run 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
776use `Function.prototype.bind` or some nice shortcut methods we provide:
777
778```javascript
779return Q.ninvoke(redisClient, "get", "user:1:id");
780return Q.npost(redisClient, "get", ["user:1:id"]);
781```
782
783You can also create reusable wrappers with `Q.denodeify` or `Q.nbind`:
784
785```javascript
786var readFile = Q.denodeify(FS.readFile);
787return readFile("foo.txt", "utf-8");
788
789var redisClientGet = Q.nbind(redisClient.get, redisClient);
790return redisClientGet("user:1:id");
791```
792
793Finally, if you're working with raw deferred objects, there is a
794`makeNodeResolver` method on deferreds that can be handy:
795
796```javascript
797var deferred = Q.defer();
798FS.readFile("foo.txt", "utf-8", deferred.makeNodeResolver());
799return deferred.promise;
800```
801
802### Long Stack Traces
803
804Q comes with optional support for “long stack traces,” wherein the `stack`
805property of `Error` rejection reasons is rewritten to be traced along
806asynchronous jumps instead of stopping at the most recent one. As an example:
807
808```js
809function theDepthsOfMyProgram() {
810 Q.delay(100).done(function explode() {
811 throw new Error("boo!");
812 });
813}
814
815theDepthsOfMyProgram();
816```
817
818usually would give a rather unhelpful stack trace looking something like
819
820```
821Error: 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
830But, if you turn this feature on by setting
831
832```js
833Q.longStackSupport = true;
834```
835
836then the above code gives a nice stack trace to the tune of
837
838```
839Error: boo!
840 at explode (/path/to/test.js:3:11)
841From previous event:
842 at theDepthsOfMyProgram (/path/to/test.js:2:16)
843 at Object.<anonymous> (/path/to/test.js:7:1)
844```
845
846Note how you can see the function that triggered the async operation in the
847stack trace! This is very helpful for debugging, as otherwise you end up getting
848only the first line, plus a bunch of Q internals, with no sign of where the
849operation started.
850
851In node.js, this feature can also be enabled through the Q_DEBUG environment
852variable:
853
854```
855Q_DEBUG=1 node server.js
856```
857
858This will enable long stack support in every instance of Q.
859
860This feature does come with somewhat-serious performance and memory overhead,
861however. If you're working with lots of promises, or trying to scale a server
862to many users, you should probably keep it off. But in development, go for it!
863
864## Tests
865
866You 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
872Copyright 2009–2017 Kristopher Michael Kowal and contributors
873MIT License (enclosed)
874