2

I am trying to write some DOM-parsing code to run from a node REPL environment. Below is a SSCCE:

"use strict";

var jsdom = require("jsdom");

var html="<a></a>";

function parse(html, x) {
    jsdom.env(html, function(errors, window) {
        x.window = window;
    });
}

var x = {};
parse(html, x);
console.log(x.window);

The idea being that after calling the parse function I would have the parsed DOM available in my x object.

When I put the above code in a file j.js and load it from the REPL I get:

> .load j.js
> "use strict";
'use strict'
> var jsdom = require("jsdom");
undefined
> var html="<a></a>";
undefined
> function parse(html, x) {
...     jsdom.env(html, function(errors, window) {
.....         x.window = window;
.....     });
... }
undefined
> var x = {};
undefined
> parse(html, x);
undefined
> console.log(x.window);
undefined
undefined
> 

Why does the code fail to assign the x.window property?

Community
  • 1
  • 1
Marcus Junius Brutus
  • 26,087
  • 41
  • 189
  • 331
  • 1
    @jsalonen I am trying to, eventually, get the DOM object corresponding to the `html` variable and do some tree navigation in it. This is a SSCCE. Maybe there's a better way to get the DOM using `jsdom` I just came up with this `parse` function as a way to abstract away the `jsdom` logic and have a way to get to communicate the value to the calling context. – Marcus Junius Brutus Feb 11 '15 at 21:00

1 Answers1

1

jsdom.env callback gets evaluated asynchronously. This means that in most cases (probably always) console.log(x.window) gets executed before the x.window = window; assignment.

The easiest fix is to pass a callback function that you execute after the assignment:

...

function parse(html, x, done) {
    jsdom.env(html, function(errors, window) {
        x.window = window;
        done();
    });
}

var x = {};

parse(html, x, function() {
    console.log(x);
});
jsalonen
  • 29,593
  • 15
  • 91
  • 109
  • OK so basically, doing it this way, the object and argument `x` are superfluous. Is there an idiom one can use to transform this asynchronous interface to a synchronous interface like: `var window = parse(html)` ?? – Marcus Junius Brutus Feb 11 '15 at 21:39
  • 1
    Yes indeed, you do not need to pass `x` as parameter to `parse` since it already is already visible from outer scope. However, writing code like `var window = parse(html)` would require you to convert from async to sync, which is generally a bad idea in node: unless you use something like node-fibers (say via sync) you would block all other execution until synchronous parse is completed. Personally, I would much rather try to go with the flow and adopt async programming style -- its the way node programs are typically written. – jsalonen Feb 11 '15 at 21:49