Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fast changes get lost while tasks are completing #33

Open
jsoverson opened this issue Dec 26, 2012 · 13 comments
Open

Fast changes get lost while tasks are completing #33

jsoverson opened this issue Dec 26, 2012 · 13 comments

Comments

@jsoverson
Copy link
Member

Watch tasks are triggered on an initial watch trigger but any changes that occur while the watch tasks are completing get ignored.

Expected :

  • Initial change immediately causes watch tasks to start
  • Any changes to files while those tasks are being completed trigger immediately after the initial run, and so on.
@shama
Copy link
Member

shama commented Dec 27, 2012

We have an option for this called interrupt: https://github.com/gruntjs/grunt-contrib-watch#optionsinterrupt Set to true to have the watch interrupt the tasks upon further changes.

@jsoverson
Copy link
Member Author

Interrupt seems like an option that still has its use on top of this issue.

The behavior described here just seems like changes are being lost/ignored which is probably never the desired result.

@shama
Copy link
Member

shama commented Dec 27, 2012

What do you mean by changes are being lost/ignored? Do you have an example?

@jsoverson
Copy link
Member Author

You can use this gruntfile to see the issue (with grunt/grunt-contrib-watch installed)

module.exports = function(grunt) {
  grunt.initConfig({
    watch: {
      gruntfile: {
        files: 'Gruntfile.js',
        tasks: ['longtask']
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-watch');

  grunt.registerTask('longtask', function(){
    var originalGrunt = grunt.file.read('Gruntfile.js');
    var done = this.async();
    setTimeout(function(){
      grunt.file.write('Gruntfile.bak', originalGrunt);
      done();
    }, 10000);
  });

  grunt.registerTask('default', 'watch');

};

If you run grunt to start watching Gruntfile.js, when that file is changed "longtask" is kicked off and doesn't complete for 10 seconds. If Gruntfile.js is saved multiple times during that period those changes never kick off another round of watch tasks, essentially being dropped.

The expectation would probably be for watch tasks to continue being run for as long as there have been no file changes since the start of the last run.

@shama
Copy link
Member

shama commented Dec 27, 2012

Thank you for the example. I still don't understand how the interrupt option doesn't fix this issue though.

Unless you're suggesting that tasks should be fired off upon every change and not interrupted. If that is the case then I don't agree. Tasks would run all over each other and devour up memory/cpu rather quickly. I think we should just keep the options we have: wait until the task finishes or cancel the previous then start again (using interrupt).

(BTW, technically your example should never fire as the pattern is for Gruntfile.js but you're creating/writing to the file Gruntfile.bak)

@jsoverson
Copy link
Member Author

The example writing to Gruntfile.bak is to showcase how changes could be lost, the changes to Gruntfile.js were still expected to be done externally.

This example shows how the task can be kicked off and, if Gruntfile.js is saved again before the task completes, the end result (Gruntfile.bak) is only based off the first changes.

The tasks would run in sequence, not immediately, they would just be queued up. In this case, if I save Gruntfile.js 8 times in 10 seconds, it would run twice, once immediately for the first change, once afterward because changes occurred since first start.

If interrupt is preferred, then I can work with that, it just seems like a harsh solution that could cause problems for more complex tasks that write files, and the current implementation makes it seem like changes are ignored. This is mostly a problem when several tasks are triggered which can easily make the run time 5-10+ seconds

@shama
Copy link
Member

shama commented Dec 27, 2012

Ah my mistake about the example. Firing on a change as well as one more time after, if further changes we're made while the task was running is a not a bad idea. Going to mark this as an enhancement. Thanks for taking the time to explain this to me.

@shama
Copy link
Member

shama commented May 1, 2013

Implemented in the reload branch. Thanks Jarrod!

@shama
Copy link
Member

shama commented May 7, 2013

@jsoverson I'll have to take another look at this later. I think it's a good idea unfortunately it causes havoc with setups that watch files that grunt is also editing. It goes into infinite loops. So I reverted it.

@russelldavis
Copy link

I took a closer look at this, and I think I see the problem. When a file changes during a run, your commit d01491b would rerun all task groups from the current run. That means you would end up rerunning tasks that didn't have any changed files, and those tasks could could change files in other watch groups, leading to the infinite recursion. If you change the logic to only rerun task groups whose files changed during the run, that should fix the infinite recursion.

This issue should be reopened, it's still a real bug.

@shama
Copy link
Member

shama commented Dec 13, 2013

I don't consider this a bug. It is a feature that isn't a high priority for me to implement. I'm happy to accept a pull request for it though.

The problem in the way of this feature is:

  1. Watch files: lib/*.js which run the task: uglify.
  2. Upon changing lib/index.js, uglify will write a file to lib/index.min.js.
  3. A change to lib/index.min.js is detected and triggers the uglify task again.
  4. Go to step 2 and repeat forever.

It is much safer (and avoids support issues here) to ignore a target while it's running tasks to prevent issues like the above (as the current behavior does). If someone wants to implement this feature, it's going to require a whole lot of tests as the watch behavior is already fairly complex.

It also will lead to yet another option for those who want to turn off this behavior. IMO, this behavior isn't worth the effort it will require but I would accept a PR for it with lots of tests.

@russelldavis
Copy link

Allow me to respectfully disagree about it not being a bug. This project's description is "Run tasks whenever watched files change". And yet here we have files being changed without their tasks being run. If a failure to reliably do that one thing isn't a bug, I don't know what is.

To be clear: I'm not asking you to personally make it a priority or to fix it. I just think it should be an open issue for others to see, be aware of, and potentially contribute to.

To address the example you gave as a concern: the correct behavior there actually is to repeat forever. That's exactly what the config in the example specifies. And in fact, if you set interrupt: true in the config, that's already what happens (I can send a demo that repro's that if you want). The way to fix the infinite repeating isn't by ignoring changes, but by fixing the watch config. In this case, you would change the watch rule in step 1 to include *.js but exclude *.min.js.

Hope that makes sense. Happy to discuss further.

@shama
Copy link
Member

shama commented Dec 13, 2013

Fair enough, reopening. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants