0

I'm working on a project using node.js. I'm pulling in a chunk of JSON data and I need to schedule jobs to write data out a serial port based on the data received. I'm using node-schedule for this task. There are several jobs that need to be scheduled and the command for each one is different. What I'm finding is that the arguments passed to the function within the scheduleJob call are interpreted at execution time rather than when scheduled within the for loop. Any ideas on how I can get the string argument to be constant?

Code Example:

var schedule = require('node-schedule');
var schedules[];
…

var startrule = new schedule.RecurrenceRule();
var start_hour = thejson.start_hour;
var start_minute = thejson.start_minute;
for (var k = 0; k < thejson.zones.length; k++)
{
  //start
  startrule.hour = start_hour;
  startrule.minute = start_minute;
  var start = schedule.scheduleJob(startrule, function(){
    writeSerial('//S' + thejson.zones[k].number + ',1');
  });
  schedules.push(start);  //upon next json update, old schedules in array are cancelled.
}

When it executes, thejson.zones[k].number fails because k is unknown. How can I get that string argument to writeSerial() to be static/constant? I've tried sticking it in an array and passing an index to writeSerial within the schedule, but that index variable is also interpreted at schedule execution.

hopper
  • 13,060
  • 7
  • 49
  • 53
Stateful
  • 737
  • 2
  • 9
  • 25
  • 1
    This is essentially the same problem as faced by countless client-side coders in setting up event handlers in a loop. – Pointy Oct 02 '13 at 17:19
  • Thank you for the pointer to the other question, Bergi. I'm new to JS and node. I've got it all working now. – Stateful Oct 02 '13 at 17:57

2 Answers2

2

You have to wrap the establishment of the scheduled job in a function to provide a per-job unique lexical scope:

  var start = schedule.scheduleJob(startrule, function(number){
    return function() {
        writeSerial('//S' + number + ',1');
    };
  }(thejson.zones[k].number));

The parameter "number" for that anonymous and immediately-invoked function ensures that the scheduled function it returns will be working with a safe copy of that part of your data structure.

Without that extra layer, each one of those jobs you scheduled shared the same variable "k". The loop in which "k" is used ends when "k" is equal to the length of the list, and so each job (when it is eventually invoked as scheduled) sees "k" pointing to a spot beyond the end of the array.

Pointy
  • 405,095
  • 59
  • 585
  • 614
1

My preferred solution would be to create a partial function:

var start = schedule.scheduleJob(startrule, function(number) {
  writeSerial('//S' + number + ',1');
}.bind(null, thejson.zones[k].number);

This creates an anonymous function which has its first argument already bound to it.

robertklep
  • 198,204
  • 35
  • 394
  • 381