Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use async/await with Gulp 4?

I'm trying to do something like this:

gulp.task("test", async () => {
    return gulp.src("**/*.scss")
        .pipe(print((filePath) => `File: ${filePath}`));
});

(print is gulp-print)

But it gives the following:

[22:08:43] Starting 'test'...
[22:08:43] Finished 'test' after 12 ms
[22:08:43] File: src\app\styles\app.scss
[22:08:43] File: src\app\styles\test.scss

i.e. It finishes before the messages are printed.

I'm using Gulp 4 (alpha 2 I think) and TypeScript (1.8.0-dev.20151204).

The generated (ES6) code looks like this:

gulp.task("test", () => __awaiter(this, void 0, Promise, function* () {
    return gulp.src("**/*.scss")
        .pipe(print((filePath) => `File: ${filePath}`));
}));

Where __awaiter is:

var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promise, generator) {
    return new Promise(function (resolve, reject) {
        generator = generator.call(thisArg, _arguments);
        function cast(value) { return value instanceof Promise && value.constructor === Promise ? value : new Promise(function (resolve) { resolve(value); }); }
        function onfulfill(value) { try { step("next", value); } catch (e) { reject(e); } }
        function onreject(value) { try { step("throw", value); } catch (e) { reject(e); } }
        function step(verb, value) {
            var result = generator[verb](value);
            result.done ? resolve(result.value) : cast(result.value).then(onfulfill, onreject);
        }
        step("next", void 0);
    });
};

Is it possible to get this working? I want to use await within my task function, but I can't do this without marking the function as asynchronous.

I'm probably missing something obvious.

like image 203
glen-84 Avatar asked Dec 09 '15 20:12

glen-84


People also ask

Is gulp async?

Did you forget to signal async completion? It is caused because in Gulp 4 all tasks are automatically asynchronous. Synchronous functions execute one after the other, and each function must wait until the previous one is completed before themselves running.

Can I use async await in node?

Async functions are available natively in Node and are denoted by the async keyword in their declaration. They always return a promise, even if you don't explicitly write them to do so. Also, the await keyword is only available inside async functions at the moment – it cannot be used in the global scope.

Does await stop the main thread?

Because await is only valid inside async functions and modules, which themselves are asynchronous and return promises, the await expression never blocks the main thread and only defers execution of code that actually depends on the result, i.e. anything after the await expression.


2 Answers

I was able to get this working with help from here and here.

gulp.task("test", async () => {
    await new Promise((resolve, reject) => {
        gulp.src("**/*.scss")
            .pipe(print((filePath) => `File: ${filePath}`))
            .on("end", resolve);
    });
});

In my real task, I had to use the finish event instead of the end event (see the 2nd link above).

It's not crystal-clear how this all works, but I think it's something along the lines of:

  1. Gulp functions are async, so they need to be awaited, but since they aren't promises, you have to wrap them in one.
  2. In order to resolve the promise, you need to listen to either the end or the finish event, and which one depends on what the last pipe returns.

Another option that appears to work is resume() followed by on("end"), in cases where finish is usually needed. This is probably because of this. I'm not sure which option is better.

I would love to understand this more, so if you're able to explain this in simple terms, please feel free to comment or post an answer.

Edit: You can also return the promise, instead of awaiting it.

like image 141
glen-84 Avatar answered Oct 06 '22 00:10

glen-84


Based on @glen-84 answer i've found my solution, it's not really different, but it's also not the same.

Somehow with the correct answer for Tasks I've got some problems when using parallel/series directly, leading again to the did you signal async completion error. Could be caused by an error i've overseen in my code.

Based on this example in the undertaker readme:

taker.task('task3', function(){
  return new Promise(function(resolve, reject){
    // do things

    resolve(); // when everything is done
  });
});

I've used this structure, which seems to work fine for task and series/parallel:

function someTask() {
    return new Promise(async (resolve, reject) => {
        await prepareSomethingAsync()
        gulp.src(somePath)
            .pipe(doingSomething())
            .pipe(await doingSomethingBasedOnAsyncResult())
            .on('finish', resolve)
            .on('error', reject)
    })
}

// e.g.: gulp.task('Some Name', someTask)

// e.g.: gulp.series(someTask)

// e.g.: gulp.parallel([someTask, someTask])
like image 32
Michael B. Avatar answered Oct 06 '22 00:10

Michael B.