3

I'm using this framework to make screenshots of several urls. The process of taking the screenshot is async, and the method does not provide a way to execute a callback, and I want to execute a callback when each screenshot is made on this script:

nightmare = new Nightmare();
urls.forEach(function (url) {
    nightmare.goto(url).screenshot(path);
});

nightmare.run(function () {
  console.log('finished all');
});

Any ideas how can I do this?

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
fernandopasik
  • 9,565
  • 7
  • 48
  • 55

2 Answers2

4

I found a way to do this, with the "use" method for executing plugins.

nightmare = new Nightmare();
urls.forEach(function (url) {
    nightmare.goto(url).screenshot(path).use(function () {
        console.log('finished one');
    });
});

nightmare.run(function () {
    console.log('finished all');
});
fernandopasik
  • 9,565
  • 7
  • 48
  • 55
3

This appears to be the purpose of the run() method. You probably want to set up and run each screenshot within the loop, since the screenshot() method relies on the phandomjs method render(), and render() is strictly synchronous (at least as of a year ago):

urls.forEach(function (url) {
    nightmare = new Nightmare();
    nightmare.goto(url).screenshot(path).run(function(err, nightmare) {
        console.log('this executes when your screenshot completes');
        // run() appropriately tears down the nightmare instance
    });
});

console.log('finished all');

You don't gain any asynchronous benefits from setting up all the screenshots at once, and "finished all" is guaranteed to only run once all screenshots have been rendered.

Alternatively, in the nightmarejs source, it looks like screenshot() does take a second done parameter that appears to be a callback, but it passes it directly into the phantomjs render() method, and as seen in the above link there was some resistance to allowing that method to take a callback.

Jason
  • 13,606
  • 2
  • 29
  • 40
  • Now that I've looked into the source rather than rely on examples, it appears that calling run in a loop will likely present issues. You can solve this by setting up a new Nightmare instance for each screenshot. Also, the `screenshot()` method *does* take a 2nd `done` parameter, that looks like a callback though it passes it in directly to phantomjs and I don't know what it does with it. The `render()` method that it's calling is synchronous, so you will have a big synchronous action in the middle of each iteration regardless. – Jason Oct 16 '14 at 19:04
  • I've updated my answer with the stuff that I've seen in the source. – Jason Oct 16 '14 at 19:10
  • 2
    PhantomJS' render doesn't take a callback but the `phantom` bridge's render does and this is what is used by Nightmare. This should solve the mystery. – Artjom B. Oct 16 '14 at 19:26
  • It does, indeed, @ArtjomB., thank you. And so there you go, you can use a callback directly. – Jason Oct 16 '14 at 19:38
  • @ArtjomB. what you said it's the answer, do you want to add it as answer? – fernandopasik Oct 17 '14 at 17:31
  • @fernandopasik Yes, I want to, but I can't right now. Re-installed node and now nothing works. :( – Artjom B. Oct 17 '14 at 17:33
  • @ArtjomB. just copy my code and add the callback to the screenshot function – fernandopasik Oct 17 '14 at 17:34
  • There's a problem, the done callback can not be overwritten as it blocks the queue of nightmarejs – fernandopasik Oct 17 '14 at 17:43