3

I'm trying to schedule a bunch of tasks to run on time intervals using changing config data as input:

let configData = initConfig();  // Initialize configuration data from file

setInterval(taskA.bind(null, configData), TASK_A_WAIT);  // Task using config data

setInterval(taskB.bind(null, configData), TASK_B_WAIT);  // Task using config data

setInterval(taskC.bind(null, configData), TASK_C_WAIT);  // Task using config data

setInterval(refreshConfig.bind(null, (error, result) => {    // Update config data
    if (error) handleError(error);
    else configData = result;
}), CONFIG_REFRESH_WAIT);

The goal is for the configuration data to update on interval using the last setInterval(), so that the first three setInterval()'s always have the latest data to work with. But will it work?

In the semantics of javascript, will the above actually bind the function to the latest configData object anew with every interval? When do binds happen in such a scenario?

TheEnvironmentalist
  • 2,694
  • 2
  • 19
  • 46
  • No, because `configData = result` overwrites the reference, not the value. The tasks will still be bound to the old reference. – Patrick Roberts Jun 06 '18 at 20:04
  • @PatrickRoberts So if I understand correctly, the binds will happen once, but because binds are by reference and the values at the appropriate references are updated with each pass of the last `setInterval()` refreshing the config data, the values for `configData` fed into the three tasks will indeed be up-to-date with every interval? – TheEnvironmentalist Jun 06 '18 at 20:07
  • binds are by value... sort of. The problem is your `configData`'s reference is changing, but the `.bind()` will hold onto the old reference. – Patrick Roberts Jun 06 '18 at 20:08
  • @PatrickRoberts So if `configData` is an object, because the references will change, it won't work. But if `configData` is not an object, it will work? Mind writing up an answer so I can upvote it? – TheEnvironmentalist Jun 06 '18 at 20:10
  • It does not matter what `configData` is, it will not work. – Patrick Roberts Jun 06 '18 at 20:10

2 Answers2

3

Since bind() isn't wrapped in another function, it is synchronously executed in-place.

Task functions are bound to original configData. If it's reassigned with configData = result, this doesn't affect bound functions.

Task functions should be wrapped with functions to get reassigned configData:

setInterval(() => { taskA(configData) }, TASK_A_WAIT)

Another option that will work with bind is to preserve same reference for configData object, this will work only if initial configData is an object:

setInterval(refreshConfig.bind(null, (error, result) => {
  ... 
  Object.assign(configData, result);
}), CONFIG_REFRESH_WAIT);

If there's a chance that configData already has properties that possibly won't be overridden, it should be cleared first.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
2

If you want the tasks to continue referring to the live version of configData, you should pass an anonymous wrapper function to each of the calls for setInterval() like this, which scopes the symbolic variable reference rather than binding the original object reference to each function:

let configData = initConfig();  // Initialize configuration data from file

setInterval(() => taskA(configData), TASK_A_WAIT);  // Task using config data

setInterval(() => taskB(configData), TASK_B_WAIT);  // Task using config data

setInterval(() => taskC(configData), TASK_C_WAIT);  // Task using config data

setInterval(refreshConfig.bind(null, (error, result) => {    // Update config data
    if (error) handleError(error);
    else configData = result;
}), CONFIG_REFRESH_WAIT);

Alternatively, if you don't want the reference to change, and you know that the configuration's structure will never change, you might prefer to make configData a const reference and use Object.assign() to do a shallow copy of result's keys, overwriting each key in configData with the new values in result:

const configData = initConfig();  // Initialize configuration data from file

setInterval(taskA.bind(null, configData), TASK_A_WAIT);  // Task using config data

setInterval(taskB.bind(null, configData), TASK_B_WAIT);  // Task using config data

setInterval(taskC.bind(null, configData), TASK_C_WAIT);  // Task using config data

setInterval(refreshConfig.bind(null, (error, result) => {    // Update config data
    if (error) handleError(error);
    else Object.assign(configData, result);
}), CONFIG_REFRESH_WAIT);
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • 1
    Hah. Didn't look above to make sure that someone else not doing the same thing, too. No wonder, there are not so many options for this to work. – Estus Flask Jun 06 '18 at 20:18
  • 1
    There's actually something really interesting in here that I didn't know about javascript. The `const` keyword can be used to make constant references, with mutable values? – TheEnvironmentalist Jun 06 '18 at 20:48