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