Docs: Add "Async Completion" documentation · gulpjs/gulp@ad8b568
1+<!-- front-matter
2+id: async-completion
3+title: Async Completion
4+hide_title: true
5+sidebar_label: Async Completion
6+-->
7+8+# Async Completion
9+10+Node libraries handle asynchronicity in a variety of ways. The most common pattern is [error-first callbacks][node-api-error-first-callbacks], but you might also encounter [streams][stream-docs], [promises][promise-docs], [event emitters][event-emitter-docs], [child processes][child-process-docs], or [observables][observable-docs]. Gulp tasks normalize all these types of asynchronicity.
11+12+## Signal task completion
13+14+When a stream, promise, event emitter, child process, or observable is returned from a task, the success or error informs gulp whether to continue or end. If a task errors, gulp will end immediately and show that error.
15+16+When composing tasks with `series()`, an error will end the composition and no further tasks will be executed. When composing tasks with `parallel()`, an error will end the composition but the other parallel tasks may or may not complete.
17+18+### Returning a stream
19+20+```js
21+const { src, dest } = require('gulp');
22+23+function streamTask() {
24+return src('*.js')
25+ .pipe(dest('output'));
26+}
27+28+exports.default = streamTask;
29+```
30+31+### Returning a promise
32+33+```js
34+function promiseTask() {
35+return Promise.resolve('some ignored value');
36+}
37+38+exports.default = promiseTask;
39+```
40+41+### Returning an event emitter
42+43+```js
44+const { EventEmitter } = require('events');
45+46+function eventEmitterTask() {
47+const emitter = new EventEmitter();
48+// Emit has to happen async otherwise gulp isn't listening yet
49+setTimeout(() => emitter.emit('finish'), 250);
50+return emitter;
51+}
52+53+exports.default = eventEmitterTask;
54+```
55+56+### Returning a child process
57+58+```js
59+const { exec } = require('child_process');
60+61+function childProcessTask() {
62+return exec('date');
63+}
64+65+exports.default = childProcessTask;
66+```
67+68+### Returning an observable
69+70+```js
71+const { Observable } = require('rxjs');
72+73+function observableTask() {
74+return Observable.of(1, 2, 3);
75+}
76+77+exports.default = observableTask;
78+```
79+80+### Using an error-first callback
81+82+If nothing is returned from your task, you must use the error-first callback to signal completion. The callback will be passed to your task as the only argument - named `done()` in the examples below.
83+84+```js
85+function callbackTask(done) {
86+// `done()` should be called by some async work
87+done();
88+}
89+90+exports.default = callbackTask;
91+```
92+93+To indicate to gulp that an error occurred in a task using an error-first callback, call it with an `Error` as the only argument.
94+95+```js
96+function callbackError(done) {
97+// `done()` should be called by some async work
98+done(new Error('kaboom'));
99+}
100+101+exports.default = callbackError;
102+```
103+104+However, you'll often pass this callback to another API instead of calling it yourself.
105+106+```js
107+const fs = require('fs');
108+109+function passingCallback(done) {
110+fs.access('gulpfile.js', done);
111+}
112+113+exports.default = passingCallback;
114+```
115+116+## No synchronous tasks
117+118+Synchronous tasks are no longer supported. They often led to subtle mistakes that were hard to debug, like forgetting to return your streams from a task.
119+120+When you see the _"Did you forget to signal async completion?"_ warning, none of the techniques mentioned above were used. You'll need to use the error-first callback or return a stream, promise, event emitter, child process, or observable to resolve the issue.
121+122+## Using async/await
123+124+When not using any of the previous options, you can define your task as an [`async` function][async-await-docs], which wraps your task in a promise. This allows you to work with promises synchronously using `await` and use other synchronous code.
125+126+```js
127+const fs = require('fs');
128+129+async function asyncAwaitTask() {
130+const { version } = fs.readFileSync('package.json');
131+console.log(version);
132+await Promise.resolve('some result');
133+}
134+135+exports.default = asyncAwaitTask;
136+```
137+138+[node-api-error-first-callbacks]: https://nodejs.org/api/errors.html#errors_error_first_callbacks
139+[stream-docs]: https://nodejs.org/api/stream.html#stream_stream
140+[promise-docs]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
141+[event-emitter-docs]: https://nodejs.org/api/events.html#events_events
142+[child-process-docs]: https://nodejs.org/api/child_process.html#child_process_child_process
143+[observable-docs]: https://github.com/tc39/proposal-observable/blob/master/README.md
144+[async-await-docs]: https://developers.google.com/web/fundamentals/primers/async-functions