-->

JavaScript variables hoisting in nodejs/async

2019-06-13 02:19发布

问题:

In the next example I don't have access to variable "locals" inside the functions "fetcher", "parser" and "saveToDb".

var parser = require('parser.js');
var fetcher = require('fetcher.js');
var saveToDb = require('models/model.js');
var async = require('async');


function task() {
    var locals = []    //<-- declared here
    async.series([
        fetcher,    //<--  can not access "locals"
        parser,     //<--  can not access "locals"
        saveToDb    //<--  can not access "locals"
    ],
            function (err) {
                if (err) return callback(err);
                callback(null);
    });
}

In the next example "local"s is accessible. I just copyed the functions declarations from the requested modules, and pasted them straight inside "async.series".

var async = require('async');

function task() {
    var locals = []    //<-- declared here
    async.series([
        function(callback) {// <-- can access "locals"},  
        function(callback) {// <-- can access "locals"},
        function(callback) {// <-- can access "locals"}
    ],
            function (err) {
                if (err) return callback(err);
                callback(null);
    });
}

While this works - I do want to keep my code modular. How can I fix that ? Or - what I forgot here about the fundamentals of JavaScript ?

Thanks.

回答1:

In the first example, the callbacks live in another scope so can't access locals.

You could create partial functions that get the locals variable passed as first argument, but that would require you to rewrite your callbacks.

// creating a partial
async.series([
  fetcher.bind(fetcher, locals),
  parser.bind(parser, locals),
  saveToDb.bind(saveToDb, locals)
], ...)

// new function signatures
function fetcher (locals, callback) { ... }
function parser  (locals, callback) { ... }
function saveToDb(locals, callback) { ... }


回答2:

You can have the 3 mentionned functions defined like this :

function fetcher(locals) {
  return function _fetcher(callback) {
    // this function body will have access to locals
  }
}

and rewrite your code as:

function task() {
    var locals = []    //<-- declared here
    async.series([
        fetcher(locals),    //<--  can now access "locals"
        parser(locals),     //<--  can now access "locals"
        saveToDb(locals)    //<--  can now access "locals"
    ],
            function (err) {
                if (err) return callback(err);
                callback(null);
    });
}


回答3:

IF i understand your problem correctly this is a javascript 101 question.

fetcher can be a factory that creates a concrete fetcher.

var fetcher = function(locals){
 locals=locals||[] // optional , assign a default , whatever ...
 return function(){
   //do some operation with locals.
   doSomething(locals);
 }
}

then in your script

 async.series([
        fetcher(locals),    //<--  will return the callback to use
        parser(locals),     //<--  will return the callback to use
        saveToDb(locals)    //<--  will return the callback to use
    ],

i believe it is a kind of currying.