Gulp: wait for pipeline to finish

Created on 31 Jul 2015  路  18Comments  路  Source: gulpjs/gulp

I am struggling a little to create a reference to a collection of files as a parameter to a plugin in another pipeline. I am not sure which way to go:

Gulp.task('collection', function() {

  return Gulp.src('some/**/*.md')
    .pipe($.frontmatter());

});

Gulp.task('templates', [ 'collection' ], function() {

  var other = Gulp.src('other/**/*.md')
    .pipe($.template({
      collection: -> reference to the finished collection
    }))

  ...
});

or better

Gulp.task('templates', [ 'collection' ], function() {

  var collection = Gulp.src('some/**/*.md')
    .pipe($.frontmatter());

  var other = Gulp.src('other/**/*.md')
    .pipe($.template({
      collection: -> reference to the finished collection
    }))

  ...
});

I would love to do this in a single task but given the pipeline architecture that doesn't seem so easy.
Any pointers much appreciated.

Most helpful comment

@tcurdt

gulp.task('task', function () {
  return Promise.all([
    new Promise(function(resolve, reject) {
      gulp.src(src + '/*.md')
        .pipe(plugin())
        .on('error', reject)
        .pipe(gulp.dest(dist))
        .on('end', resolve)
    }),
    new Promise(function(resolve, reject) {
      gulp.src(src + '/*.md')
        .pipe(plugin())
        .on('error', reject)
        .pipe(gulp.dest(dist))
        .on('end', resolve)
    })
  ]).then(function () {
    // Other actions
  });
});

All 18 comments

@tcurdt

gulp.task('task', function () {
  return Promise.all([
    new Promise(function(resolve, reject) {
      gulp.src(src + '/*.md')
        .pipe(plugin())
        .on('error', reject)
        .pipe(gulp.dest(dist))
        .on('end', resolve)
    }),
    new Promise(function(resolve, reject) {
      gulp.src(src + '/*.md')
        .pipe(plugin())
        .on('error', reject)
        .pipe(gulp.dest(dist))
        .on('end', resolve)
    })
  ]).then(function () {
    // Other actions
  });
});

@TrySound oh - that easy?

I thought that only returning a pipeline would run it. What runs the pipeline like this?
Doesn't gulp.src().pipe().pipe().on() just create the pipeline?

Doesn't it need to be

gulp.task('task', function () {

  var collection;

  return Promise.all([
    new Promise(function(resolve, reject) {
      collection = gulp.src(src + '/*.md')
        .pipe(plugin())
        .on('error', reject)
        .pipe(gulp.dest(dist))
        .on('end', resolve)
    })
  ]).then(function () {

    var other = Gulp.src('other/**/*.md')
      .pipe($.template({
        collection: -> reference to the finished collection
      }))

    return merge(collection, other);

  });
});

Mm.. No. You just need to say your task when it ends. You can return stream or promise or use callback of task.

@TrySound but this doesn't even associate the the pipeline with the promise:

new Promise(function(resolve, reject) {
  gulp.src(src + '/*.md')
    .pipe(plugin())
    .on('error', reject)
    .pipe(gulp.dest(dist))
    .on('end', resolve)
})

In the promise it builds the pipeline but it's not connected to the task via promise.
Is it maybe just missing a return?

@tcurdt If you want to make a few tasks in parallel use Promise.all, in series use promise reducing. Returning just one promise is the same as returning stream.

Or what about you?

@TrySound hm - interesting. just tried this

gulp.task('test', function() {

  gulp.src("design/**/*")
    .pipe($.tap(function(file){
      console.log(file.path);
    }));

  return null;
});

and it does run the pipeline. I don't understand how/why yet - but seems like you are right then.

@tcurdt Task finished before pipeline.

Right - but I am not getting what starts the pipeline yet.
Maybe need to dig into the source to understand this :-/

Creating the pipeline starts the pipeline, it runs immediately. This is how streams work, check the node docs for more info.

So something like this should actually be quite easily possible:

// execute pipeline and return files when finished
var collection = collect(function(){
  return gulp.src(src + '/*.md')
    .pipe($.frontmatter());
});

Gulp.src('other/**/*.md')
  .pipe($.template({
    collection: collection
  }))
  ...

I have tried it like this - but the pipe does not fire the error/end handlers.

function pipeToCollection(pipe) {
  var collection = [];
  new Promise(function(resolve, reject) {

    console.log("create collection");

    pipe()
    .on('error', reject)
    // add to collection
    .on('end', resolve)

  }).then(function(){

    console.log("create collection - done");

  });
  return collection;
}

var posts = pipeToCollection(function(){
  return gulp.src('content/posts/**/*.md', { base: 'content/posts' })
    .pipe($.frontmatter());
});

While I the end/error handlers are now firing it became obvious (doh!) that the function (of course) returns before the then and the end of the pipeline. So that feels a little like back to square one.

I guess I could try with https://github.com/luciotato/waitfor but is there really no better way?

Actually - I guess this should work:

function pipeToCollection(pipe, callback) {

  var collection = [];

  pipe()
  .on('error', function(){
    // error handling
  })
  .pipe($.tap(function(file){
    collection.push(file)
  }))
  .on('end', function(){
    callback(collection)
  })
}

pipeToCollection(function(){
  return gulp.src('...'})
    .pipe($.frontmatter())
}, function(posts){

  var pages = gulp.src('...')
    .pipe($.template({
    posts: posts
  }))

});

Only gets ugly (because of the nesting) when there are multiple collections used.

Yes, streams are asynchronous. Treat them like any other async thing, use a module (promises, async, whatever you want) to collect data from multiple streams.

function Async(p) {
   return new Promise((res, rej) => p.on('error', err => rej(err)).on('end', () => res()));
}

gulp.task('task', async () => {

  await Async(gulp.src(src + '/*.md')
      .pipe(plugin())
      .pipe(gulp.dest(dist)));

  await Async(gulp.src(src + '/*.md')
      .pipe(plugin())
      .pipe(gulp.dest(dist)));
});
Was this page helpful?
0 / 5 - 0 ratings