OverZealous / run-sequence

Run a series of dependent gulp tasks in order
MIT License
961 stars 56 forks source link

Is there a way to make subtasks run in sequence? #59

Closed x-yuri closed 8 years ago

x-yuri commented 8 years ago

I've run into an issue with laravel-elixir, where the next task starts before the previous one finishes. It appears it wraps my tasks into dynamically generated ones, so it basically comes down to this:

var gulp = require('gulp');
var runSequence = require('run-sequence');
var fs = require('fs');

gulp.task('default', function() {
    runSequence('t1-wrapper', 't2-wrapper');
});

gulp.task('t1-wrapper', function() {
    return gulp.start('t1');
});

gulp.task('t1', function() {
  return gulp.src('1/1.js')
    .pipe(gulp.dest('2'));
});

gulp.task('t2-wrapper', function() {
    return gulp.start('t2');
});

gulp.task('t2', function() {
  // setTimeout(function() {
    console.log(fs.existsSync('2/1.js'));
  // }, 100);
  return gulp.src('2/1.js')
    .pipe(gulp.dest('3'));
});

Do once npm install gulp && mkdir 1 && touch 1/1.js and before each run do rm {2,3}/1.js.

When I uncomment setTimeout, console.log starts outputting true.

Is there a way to make it work with subtasks?

OverZealous commented 8 years ago

I have no idea what is going on here. That example script is a mess.

run-sequence can only work with tasks that are correctly configured to be asynchronous. They either need to return a promise or stream, or they need to call the callback. Otherwise they are assumed to be synchronous.

A task that uses run-sequence can use the callback parameter to run asynchronously (example is in the docs).

I have absolutely no idea what you are doing with the "wrapper" functions, but if you need to run an async task outside the normal Gulp dependencies, you can use run-sequence for that (in the form of runSequence('taskName', callback), which will at least block until taskName completes.

x-yuri commented 8 years ago

Sorry for being unable to come up with a simpler script that reproduces the issue.

Let me try again. What I'm asking here is, "Is there a way to run tasks t1-wrapper and t2-wrapper in sequence, so that t2-wrapper starts after t1 finishes, where t1 is the task started by t1-wrapper?"

I see no way to turn gulp task into a promise or a stream, so callback is indeed the only option I see. And this way it works:

gulp.task('t1-wrapper', function(cb) {
  runSequence('t1', cb);
});

Not sure what you meant by "which will at least block until taskName completes". It sounds as if there are some pitfalls with using it this way.

AFAICS, they are deprecating gulp.run and supposedly gulp.start (which, I guess, never was part of public API). So, using run-sequence must be the best option here. Unless, laravel-elixir can use dependencies for this. My guess here is that nested tasks better reflect what's going on, compared to dependencies. More semantically correct.

The bigger picture is that laravel-elixir introduces its own tasks. They get turned into gulp tasks, which run with run-sequence. And some of these gulp tasks need to call other gulp tasks. And here's why. There's laravel-elixir task called task, which is meant to call some gulp task by name. Simple as that.

OverZealous commented 8 years ago

What I don't understand is why you have a task that starts a single task. ie: why is gulp.start being used at all?

If you want to perform some task after a task completes, you can use the built-in Gulp dependencies (which I recommend wherever possible over run-sequence. run-sequence is only intended to fill in that situation where dependencies won't work). This looks like gulp.task('t1-wrapper', ['t1'], function() ....

Otherwise, using run-sequence works if you need to run a task and need to know when it completes.

x-yuri commented 8 years ago

First off, let us get it out of the way, it's not me who does this. It's laravel-elixir. I haven't contributed to this package as of yet. That is, the code I provided in the original post is not what exactly happens. It's what basically happens.

laravel-elixir introduces a level of abstraction. With it you can do:

elixir(function(mix) {
    mix.sass('app.scss')
        .coffee(['app.coffee', 'controllers.coffee']);
});

Under the hood, when you run gulp, this is turned on the fly into 2 gulp tasks, which compile your assets. (default task runs the just generated tasks with run-sequence.) But when you do this:

elixir(function(mix) {
    mix.task('t1').task('t2');
});

It's basically turned into 2 gulp tasks again (the ones I called t1-wrapper and t2-wrapper). But the first task starts gulp task t1, and the second one, t2. Because that's what laravel-elixir's task task is for. It calls some other existing gulp task.

Finally, to answer your question. This might have been implemented with dependencies. Not sure how much more complex is that, if at all. But starting other task seems more semantically correct here from laravel-elixir's point of view. Since it's not really a dependency, it's laravel-elixir's task that runs gulp task.

OverZealous commented 8 years ago

OK, I don't know what any of this has to do with run-sequence. My library is very simple, it just starts gulp tasks and waits for them to complete. If those tasks are incorrectly configured, and they return without a callback, promise, or stream before they are actually complete, how would it know? How would gulp know?

Don't use laravel-elixir, whatever that is, I guess, or call the inner tasks directly.

x-yuri commented 8 years ago

I had 2 assumptions in mind, when I started the question: either run-sequence doesn't do its job properly, or laravel-elixir is not properly starting subtasks. It appears to be the latter. Thanks for helping me figure that out. They are supposed to use run-sequence for starting subtasks, as in the example above.

Concerning the workaround, I already has one.