tweeeetyのぶろぐ的めも

アウトプットが少なかったダメな自分をアウトプット<br>\(^o^)/

【gulp】run-sequenceでtaskを並列/直列(同期的)にする挙動を確認する

はじめに

gulpは長い事つかってますが、 フロントの方が入れてくれた環境を基に使っているのでゼロから学んだ経験は乏しいです。 (自分はサーバサイド)

ディレクトリのclearやbuildを行うためにrun-sequenceを使っています。

ふとした動機でこれの挙動をちゃんと確認したくなったので
そんなときの自分メモ

f:id:tweeeety:20180531172527p:plain

アジェンダ

  1. run-sequenceとは
  2. run-sequenceの使い方
  3. run-sequenceのrunSequenceで直列指定の挙動を確認

1. run-sequenceとは

説明は公式からの引用です

Runs a sequence of gulp tasks in the specified order.
This function is designed to solve the situation where you have defined run-order,
but choose not to or cannot use dependencies.
 
https://www.npmjs.com/package/run-sequence

指定された順序で一連のタスクを実行する君ということですね。

gulp4.0

公式サイトからの引用です。

これは、同様にタスクの依存関係を定義するためのサポートを持つ必要がある、
gulp 4.0のリリースまでの一時的な解決策であることを意図していました。
 
https://www.npmjs.com/package/run-sequence

公式サイトPlease Noteに記載がありますが、
gulp4.0ではgulp.seriesgulp.parallelという機能で同様の事ができるらしいです。

ちなみに、gulp4.0のchangelogには記載があります。

2. run-sequenceの使い方

インストール

$ npm install --save-dev run-sequence

使い方

公式からの引用です。
https://www.npmjs.com/package/run-sequence#usage

var gulp = require('gulp');
var runSequence = require('run-sequence');
var del = require('del');
var fs = require('fs');
 
// This will run in this order:
// * build-clean
// * build-scripts and build-styles in parallel
// * build-html
// * Finally call the callback function
gulp.task('build', function(callback) {
  runSequence('build-clean',
              ['build-scripts', 'build-styles'],
              'build-html',
              callback);
});
 
// configure build-clean, build-scripts, build-styles, build-html as you wish,
// but make sure they either return a stream or promise, or handle the callback
// Example:
 
gulp.task('build-clean', function() {
    // Return the Promise from del()
    return del([BUILD_DIRECTORY]);
//  ^^^^^^
//   This is the key here, to make sure asynchronous tasks are done!
});
 
gulp.task('build-scripts', function() {
    // Return the stream from gulp
    return gulp.src(SCRIPTS_SRC).pipe(...)...
//  ^^^^^^
//   This is the key here, to make sure tasks run to completion!
});
 
gulp.task('callback-example', function(callback) {
    // Use the callback in the async function
    fs.readFile('...', function(err, file) {
        console.log(file);
        callback();
//      ^^^^^^^^^^
//       This is what lets gulp know this task is complete!
    });
});

ポイント

上記のコードでいうと、ポイントは以下です。

  runSequence('build-clean',
              ['build-scripts', 'build-styles'],
              'build-html',
              callback);
  • runSequenceに記載した順に直列で実行される
  • 各タスクはreturnするか、callback()を呼び出す必要がある

3. run-sequenceのrunSequenceで直列指定の挙動を確認

まず、非同期な処理想定としてsetTimeoutを使って確認してみます。

runSequenceを使うパターンを以下のようなパターンで確認します。

  • 3.0. runSequence使った場合の期待する挙動
  • 3.1. runSequence使わなかった場合
  • 3.2. runSequencee使ったがcallback呼ばなかった場合
  • 3.3. runSequencee使ってcallbackを入れたがおしい場合
  • 3.4. runSequencee使って意図通りの指定をした場合

3.0. runSequencee使った場合の期待する挙動

まずは期待する挙動です。
以下のようにtaskを定義したとします。

f:id:tweeeety:20180531164424p:plain

この時、呼ばれる順番を以下のように期待したという前提ですすめます。
f:id:tweeeety:20180531164445p:plain

3.1. runSequence使わなかった場合

runSequenceを使わなかった場合の挙動を確認します。

コード
var gulp = require('gulp');                                                                                                                                                                                                                                

gulp.task('task', ['task-1', 'task-2', 'task-3']);

gulp.task('task-1', function() {
  console.log('called task-1');
});

gulp.task('task-2', ['task-2-1', 'task-2-2']);

gulp.task('task-2-1', function() {
  console.log('called task-2-1');

  // 5秒間かかるタスク
  setTimeout(function(){
    console.log("\tcalled task-2-1_subtask");
  }, 5000);
});

gulp.task('task-2-2', function() {
  console.log('called task-2-2');

  // 3秒間かかるタスク
  setTimeout(function(){
    console.log("\tcalled task-2-2_subtask");
  }, 3000);
});

gulp.task('task-3', function() {
  console.log('called task-3');

  // 1秒間かかるタスク
  setTimeout(function(){
    console.log("\tcalled task-3_subtask");
  }, 1000);

});

gulp.task('default', ['task-normal']);

このコードは以下に置きました。
https://github.com/tweeeety/gulp-task-order/blob/master/task_normal/gulpfile.js

実行してみる

このまま実行すると、*_subtaskが非同期に最後にまとめて呼ばれます。

$ gulp task
[16:39:10] Using gulpfile ~/gulp-task-order/task_normal/gulpfile.js
[16:39:10] Starting 'task-1'...
called task-1
[16:39:10] Finished 'task-1' after 104 μs
[16:39:10] Starting 'task-2-1'...
called task-2-1
[16:39:10] Finished 'task-2-1' after 592 μs
[16:39:10] Starting 'task-2-2'...
called task-2-2
[16:39:10] Finished 'task-2-2' after 558 μs
[16:39:10] Starting 'task-2'...
[16:39:10] Finished 'task-2' after 2 μs
[16:39:10] Starting 'task-3'...
called task-3
[16:39:10] Finished 'task-3' after 38 μs
[16:39:10] Starting 'task'...
[16:39:10] Finished 'task' after 1.78 μs
  called task-3_subtask
  called task-2-2_subtask
  called task-2-1_subtask
結果

以下のような順で呼ばれています

f:id:tweeeety:20180531164537p:plain

ポイント
  • *_subtaskたちが非同期にすべて最後に呼ばれている
  • 順番もtask-3-1_subtask, task-2-2_subtask, task-2-1_subtaskというように期待した逆の順に呼ばれている

3.2. runSequence使ったがcallback呼ばなかった場合

runSequenceは使ったが、callbackを呼ばなかった場合の挙動を確認します。
一番よくありがちなパターンでしょうか。

コード
var gulp = require('gulp');
var runSequence = require('run-sequence');

gulp.task('task', function(callback) {
  runSequence('task-1', 'task-2', 'task-3', callback);
});
                                                                                                                                                                                                                                                           
gulp.task('task-1', function() {
  console.log('called task-1');
});

gulp.task('task-2', ['task-2-1', 'task-2-2']);

gulp.task('task-2-1', function() {
  console.log('called task-2-1');

  // 5秒間かかるタスク
  setTimeout(function(){
    console.log("\tcalled task-2-1_subtask");
  }, 5000);
});

gulp.task('task-2-2', function() {
  console.log('called task-2-2');

  // 3秒間かかるタスク
  setTimeout(function(){
    console.log("\tcalled task-2-2_subtask");
  }, 3000);
});

gulp.task('task-3', function() {
  console.log('called task-3');

  // 1秒間かかるタスク
  setTimeout(function(){
    console.log("\tcalled task-3_subtask");
  }, 1000);

});

gulp.task('default', ['task-sync-fail-1']);

このコードは以下に置きました。
https://github.com/tweeeety/gulp-task-order/blob/master/task_sync_fail_1/gulpfile.js

実行してみる

runSequenceを使ったものの、
3.1. runSequence使わなかった場合 とまったく同じ結果で非同期なままです。

$ gulp task
[17:02:17] Using gulpfile ~/gulp-task-order/task_sync_fail_1/gulpfile.js
[17:02:17] Starting 'task'...
[17:02:17] Starting 'task-1'...
called task-1
[17:02:17] Finished 'task-1' after 102 μs
[17:02:17] Starting 'task-2-1'...
called task-2-1
[17:02:17] Finished 'task-2-1' after 967 μs
[17:02:17] Starting 'task-2-2'...
called task-2-2
[17:02:17] Finished 'task-2-2' after 901 μs
[17:02:17] Starting 'task-2'...
[17:02:17] Finished 'task-2' after 2.05 μs
[17:02:17] Starting 'task-3'...
called task-3
[17:02:17] Finished 'task-3' after 40 μs
[17:02:17] Finished 'task' after 5.69 ms
  called task-3_subtask
  called task-2-2_subtask
  called task-2-1_subtask
結果

以下のような順で呼ばれています

f:id:tweeeety:20180531170801p:plain

ポイント
  • 3.1. runSequence使わなかった場合 とまったく同じ

3.3. runSequencee使ってcallbackを入れたがおしい場合

runSequenceeを使ってcallbackも入れたがネストが考慮されてない場合の挙動を確認してみます。

コード
var gulp = require('gulp');
var runSequence = require('run-sequence');

gulp.task('task', function(callback) {                                                                                                                                                                                                                     
  runSequence('task-1', 'task-2', 'task-3', callback);
});

gulp.task('task-1', function() {
  console.log('called task-1');
});

gulp.task('task-2', ['task-2-1', 'task-2-2']);

gulp.task('task-2-1', function(callback) {
  console.log('called task-2-1');

  // 5秒間かかるタスク
  setTimeout(function(){
    console.log("\tcalled task-2-1_subtask");
    callback();
  }, 5000);
});

gulp.task('task-2-2', function(callback) {
  console.log('called task-2-2');

  // 3秒間かかるタスク
  setTimeout(function(){
    console.log("\tcalled task-2-2_subtask");
    callback();
  }, 3000);
});

gulp.task('task-3', function(callback) {
  console.log('called task-3');

  // 1秒間かかるタスク
  setTimeout(function(){
    console.log("\tcalled task-3_subtask");
    callback();
  }, 1000);

});

gulp.task('default', ['task-sync-fail-2']);

このコードは以下に置きました。
https://github.com/tweeeety/gulp-task-order/blob/master/task_sync_fail_2/gulpfile.js

実行してみる

よく見ないとわかりずらいですが、
task-2-2_subtasktask-2-1_subtaksあたりが意図した通りになっていません。

$ gulp task
[17:13:16] Using gulpfile ~/gulp-task-order/task_sync_fail_2/gulpfile.js
[17:13:16] Starting 'task'...
[17:13:16] Starting 'task-1'...
called task-1
[17:13:16] Finished 'task-1' after 163 μs
[17:13:16] Starting 'task-2-1'...
called task-2-1
[17:13:16] Starting 'task-2-2'...
called task-2-2
  called task-2-2_subtask
[17:13:19] Finished 'task-2-2' after 3 s
  called task-2-1_subtask
[17:13:21] Finished 'task-2-1' after 5 s
[17:13:21] Starting 'task-2'...
[17:13:21] Finished 'task-2' after 4.19 μs
[17:13:21] Starting 'task-3'...
called task-3
  called task-3_subtask
[17:13:22] Finished 'task-3' after 1 s
[17:13:22] Finished 'task' after 6.01 s
結果

以下のような順で呼ばれています
f:id:tweeeety:20180531172026p:plain

ポイント
  • task2とtask3の*_subtaskは、それぞれの中で呼ばれるようになった
  • ただし、task2内のtask-2-2_subtask、task-2-1_subtaksの順番は非同期のまま

3.4. runSequencee使って意図通りの指定をした場合

最後に意図通りの使い方の確認です。

コード
var gulp = require('gulp');
var runSequence = require('run-sequence');

gulp.task('task', function(callback) {
  runSequence('task-1', 'task-2', 'task-3', callback);
});

gulp.task('task-1', function() {
  console.log('called task-1');
});

gulp.task('task-2', function(callback){                                                                                                                                                                                                                    
  runSequence('task-2-1', 'task-2-2', callback);
});

gulp.task('task-2-1', function(callback) {
  console.log('called task-2-1');

  // 5秒間かかるタスク
  setTimeout(function(){
    console.log("\tcalled task-2-1_subtask");
    callback();
  }, 5000);
});

gulp.task('task-2-2', function(callback) {
  console.log('called task-2-2');

  // 3秒間かかるタスク
  setTimeout(function(){
    console.log("\tcalled task-2-2_subtask");
    callback();
  }, 3000);
});

gulp.task('task-3', function(callback) {
  console.log('called task-3');

  // 1秒間かかるタスク
  setTimeout(function(){
    console.log("\tcalled task-3_subtask");
    callback();
  }, 1000);

});

gulp.task('default', ['task-sync']);

このコードは以下に置きました。
https://github.com/tweeeety/gulp-task-order/blob/master/task_sync/gulpfile.js

実行してみる

意図どおりになりました!

$ gulp task
[17:21:37] Using gulpfile ~/gulp-task-order/task_sync/gulpfile.js
[17:21:37] Starting 'task'...
[17:21:37] Starting 'task-1'...
called task-1
[17:21:37] Finished 'task-1' after 102 μs
[17:21:37] Starting 'task-2'...
[17:21:37] Starting 'task-2-1'...
called task-2-1
  called task-2-1_subtask
[17:21:42] Finished 'task-2-1' after 5 s
[17:21:42] Starting 'task-2-2'...
called task-2-2
  called task-2-2_subtask
[17:21:45] Finished 'task-2-2' after 3 s
[17:21:45] Finished 'task-2' after 8.01 s
[17:21:45] Starting 'task-3'...
called task-3
  called task-3_subtask
[17:21:46] Finished 'task-3' after 1 s
[17:21:46] Finished 'task' after 9.01 s
結果

無事意図通りに呼ばれるようになりました。 f:id:tweeeety:20180531172423p:plain

サンプル

一応サンプルをおいておきました。
npm installすればそれぞれ試せます。

参考

おわり

使い方や説明を見てわかることもありますが、
実際に確認するのって大切ですよね\(^o^)/