0

I am familiar with asynchronous programming and using callbacks, however I am not as knowledgeable in using promises. I have the following code:

page.evaluate(function() {
        elements.push(document.getElementById('form_username'));
        elements.push(document.getElementById('form_password'));
        return elements;

    }).then(function(html){
        console.log(html[0].className);

    }).then( () => { 
        _page.close();
        _ph.exit();    

    }).catch(err => console.log(err));

I am getting an error saying that 0 doesnt exist for type undefined when I pass it to 'html' and try to get the className, and I believe that is the case because when I return elements it doesnt wait the HTML elements to get pushed in because it is Asynchronous. I know that I can use callback(elements) if I were using callbacks, but I am not sure what to do in this case.

EDIT: page.evaluate() is a function used by phantomjs-node, found here https://github.com/amir20/phantomjs-node. Also this is just a piece of the code. When returning just a single HTML element I am able to get its className. Like this:

page.evaluate(function() {
        return document.getElementById('form_username');

    }).then(function(html){
        console.log(html.className);

    }).then( () => { 
        _page.close();
        _ph.exit();    

    }).catch(err => console.log(err));
  • What is `elements` declared as? Is it a regular array? Or something different? `.push()` on a regular array is NOT asynchronous. – jfriend00 Jan 03 '18 at 18:00
  • Are you 100% sure that `document.getElementById('form_username')` is returning a valid DOM element. To debug, you should check that before doing a `.push()` on it. – jfriend00 Jan 03 '18 at 18:02
  • I'm not sure about my conclusion, but I just want to point out that `elements` is not returning a valid Promise so that the `.then` the function will be not executed as expected – Felix Fong Jan 03 '18 at 18:09
  • @FelixFong - `return elements` is going back to the `page.evaluate()` callback (whatever that is). For the `.then()` handler to work, `page.evaluate()` needs to return a promise. – jfriend00 Jan 03 '18 at 18:14
  • 1
    What is `page.evaluate()`? Can you point to some doc for that? – jfriend00 Jan 03 '18 at 18:39
  • IIRC the `evaluate` function of phantomjs does not support closures. – Bergi Jan 03 '18 at 19:01
  • @jfriend00 I am using page.evaluate from the node-phantom npm library. https://github.com/amir20/phantomjs-node This is only a piece of the code. elements is declared as an empty array. – Haroun Ansari Jan 03 '18 at 19:05
  • @Bergi what do you mean that it does not support closures? – Haroun Ansari Jan 03 '18 at 19:11
  • @HarounAnsari [The docs state](http://phantomjs.org/api/webpage/method/evaluate.html) that "*The arguments and the return value to the evaluate function must be a simple primitive object. The rule of thumb: if it can be serialized via JSON, then it is fine. Closures, functions, DOM nodes, etc. will not work!*" – Bergi Jan 03 '18 at 19:33
  • @Bergi so I should be able to return it all as an object instead of an array then. I will try it out. – Haroun Ansari Jan 03 '18 at 19:50
  • No, an array should work as well. You just must create it within the evaluated function. – Bergi Jan 03 '18 at 19:55
  • I defined the array/object within the evaluated function and it is still getting undefined when passed to the next .then() – Haroun Ansari Jan 03 '18 at 19:57

1 Answers1

0

I don't know what elements is in your evaluate. However, you really should just return something like this instead:

page.evaluate(function() {
  return [document.getElementById('form_username'), document.getElementById('form_password')];
})

And as others have suggested, I am not too sure if returning DOM objects works. It will serialize to JSON just fine but you can't call any DOM objects. I.e the above would not work with elements[0].getAttrinute('name'). Only data is passed, because phantomjs is a different process. So all data is serialized.

It would be best to pass what you need. Something like this:

return [document.getElementById('form_username').className, document.getElementById('form_password').className]

Disclaimer: I am amir20 on Github and the owner of this repo.

If you want to be happier in life, I highly recommend using async/await instead of Promises.

Amir Raminfar
  • 33,777
  • 7
  • 93
  • 123