Node: Erratic behaviour with async/await and Nightmare

Created on 8 Nov 2017  路  7Comments  路  Source: nodejs/node

Issue moved here: https://github.com/segmentio/nightmare/issues/1314

  • Version: 7.6.0 -> 9.1.0
  • Platform: macOS 10.12
  • Subsystem: async/await


Given the following code:

const Nightmare = require('nightmare'); // 2.10.0

let thatSpecialPlaceInMemory;
const makeNightmare = async () => {

  const n = new Nightmare({ show: true });

  await n.useragent('custom ua');
  await n.goto('about:blank');

  thatSpecialPlaceInMemory = n;
  console.log('n exists? before return', !!n);
  return n;
};

const x = async () => {

  const n = await makeNightmare();
  console.log('n exists? after return', !!n);

  if (n !== thatSpecialPlaceInMemory) {
    throw 'return value was incorrect'
  }

  await thatSpecialPlaceInMemory.end()
};

x()
  .then(console.log)
  .catch(e => {
    console.error(e);
    thatSpecialPlaceInMemory.end().then(_ => _);
  });

What happens:

  • x calls makeNightmare
  • makeNightmare initiates nightmare and returns the object, and keeps a global copy of it (for testing down the line)
  • when control is handed back to x the return value of function makeNightmare is now undefined, despite the fact that thatSpecialPlaceInMemory is still an object

Expected result: n should definitely be equal to thatSpecialPlaceInMemory


Given the following variation of the above code, where makeNightmare has been altered to be syncronous

const Nightmare = require('nightmare'); // 2.10.0

let thatSpecialPlaceInMemory;
const makeNightmare = () => {

  const n = new Nightmare({ show: true });

  thatSpecialPlaceInMemory = n;
  console.log('n exists? before return', !!n);
  return n;
};

const x = async () => {

  const n = makeNightmare();
  console.log('n exists? after return', !!n);

  if (n !== thatSpecialPlaceInMemory) {
    throw 'return value was incorrect'
  }

  await thatSpecialPlaceInMemory.end()
};

x()
  .then(console.log)
  .catch(e => {
    console.error(e);
    thatSpecialPlaceInMemory.end().then(_ => _);
  });

The return value of makeNightmare is equal to thatSpecialPlaceInMemory when control is handed back to function x, as expected.

cc @segmentio
I could not replicate this with something like a simple setTimeout wrapped in a promise, not sure if this is a core node issue or something in the nightmare package is making node act erratically.

question wrong repo

All 7 comments

not sure what the problem is, but here is a simplified version of what you are interested in. async functions and Promises behave the same:

const Nightmare = require('nightmare'); // 2.10.0

(async () => {
  const n = await Promise.resolve(new Nightmare());
  console.log(n);  // -> undefined
})();

Promise.resolve(new Nightmare()).then(x => console.log(x)); // -> undefined

I'm a little confused why this is being opened in Node core. This seems like an issue with Nightmare and not Node core.

The Promise.resolve(value) method returns a Promise object that is resolved with the given value. If the value is a thenable (i.e. has a "then" method), the returned promise will "follow" that thenable, adopting its eventual state; if the value was a promise, that object becomes the result of the call to Promise.resolve; otherwise the returned promise will be fulfilled with the value.

https://github.com/segmentio/nightmare/blob/7d1b8a40b6f5857a06e7f20440fc4f416c54e9a1/lib/nightmare.js#L487-L498

Yes, I agree. I wasn't sure where the problem was. Will reopen issue on the repo's side.

In general it seems like the library isn't quite setup to handle async/await or Promises very well. Looking at the issue tracker, there seem to be a whole lot of similar questions (all with their own twist).

Thanks @apapirovski for looking into it 馃槃

FWIW if you want your code above to work, you could probably just return something like {nightmare: n} from makeNightmare and then do this instead: const { nightmare: n } = await makeNightmare();.

If that (or something similar) doesn't work, feel free to reopen as it might indicate there is an issue in Node too.

(That solution is probably more likely than Nightmare changing their API to not have a then method. :))

didn't know _new Nightmare()_ returns a Promise:

here's a short version of async/await, Promise resolution working (though I don't know any of the inner workings/intentions of the library - was just looking at the docs):

const Nightmare = require('nightmare'); // 2.10.0

new Nightmare().end(t => 'test').then(x => console.log(x));   // test

(async () => {
  const x = await new Nightmare().end(t => 'test');
  console.log(x);   // test
})();
Was this page helpful?
0 / 5 - 0 ratings