简体   繁体   中英

Wait for a variable to exist then do something in javascript

I have a script that will be executed before the variable it must read will be loaded..

this is my script that will be executed first

funcThatWaits(varToWait).ready(function(){
        //callback time!!
    alert("varToBeWait is Ready!!");
});

and this is the script that will be next to be loaded

var varToWait=null;

All I want is to create a function that will wait to a variable to exist and do a callback automatically when it detects the variable already existed.(This means while the variable does not exist it will wait)

Is this possible? my first script is quite copied on the $(document).ready() function of jquery that waits for the DOM to be fully loaded... is this possible for JS variables?

If your variable comes from another function (and maybe from another scope), then you can pass a callback and provide it the variable when the second function executes the callback. you don't need to wait when it will exist, but you will wait until the second script provides it for you .

//in the second script:

var varIWant = 'foo'

function fromSecondScript(callback){
    callback(varIWant);
}

//in the first script:

function fromFirstScript(){
    fromSecondScript(function(theVar){
        //"theVar" in this function scope is "varIWant" from the other scope
    })
}

another way to do it is to have a loader script defined beforehand to aggregate callbacks and call them once their variables are set:

var aggregator = (function(){
    var stored = {};

    return {
        //adds a callback to storage
        addCallback : function(varName,callback){
            if(!stored.hasOwnProperty(varName)){
                stored[varName] = [];
            }
            stored[varName].push(callback);
        },
        //executes stored callbacks providing them data
        execute : function(varName,data){
            if(stored.hasOwnProperty(varName)){
                for(var i=0;i<stored[varName].length;i++){
                    stored[varName][i](data)
                }
            }
        }
}());

//in the first script add callbacks. you can add any number of callbacks
aggregator.addCallback('VarExists',function(theVar){
    //do what you want when it exists
});

aggregator.addCallback('VarExists',function(theVar){
    //another callback to execute when var exists
});

//in the second script, execute the callbacks of the given name
aggregator.execute('VarExists',theVarYouWantToShare);

Intro

The only way I've found is to create a fully-fledged tracking mechanism.

The currently accepted answer does the job, but I personally like to hide implementation details a bit more.

Preparation

We'll create a class to keep track of things.

/* ChangeTracker.js */

// Mechanism to keep track of when a variable is set.
class ChangeTracker {
  constructor() {
    this._value = null;            // <- the var we're waiting for
    this._valueAlreadySet = false; // <- used to track first set
    this._singleListeners = [];    // <- used for one-off requests
    this._multiListeners = [];     // <- used for recurring requests
  }

  // Using this, you'll be notified the first time the value changes. If the
  // value has already been set, you'll be notified immediately.
  getOnce(callback) {
    if (this._valueAlreadySet) {
      callback(this._value);
    }
    else {
      this._singleListeners.push(callback);
    }
  }

  // Using this, you'll be notified every time the value changes. If the value
  // has already been set, you'll be notified immediately.
  getEveryChange(callback) {
    this._multiListeners.push(callback);
    if (this._valueAlreadySet) {
      callback(this._value);
    }
  }

  // Sets the value, then notifies those waiting for it.
  setValue(value) {
    this._value = value;
    if (!this._valueAlreadySet) {
      this._valueAlreadySet = true;

      // Notify all one-off listeners.
      const singleListeners = this._singleListeners;
      for (let i = 0; i < singleListeners.length; i++) {
        singleListeners[i](value);
      }

      // Mark for garbage collection.
      this._singleListeners = null;
    }

    // Notify all subscribers.
    for (let i = 0; i < this._multiListeners.length; i++) {
      this._multiListeners[i](value);
    }
  }
}

Data source

We'll be loading a spaceship. It will eventually have our variable, but it's asynchronous and takes long to initialise.

/* shipLoader.js */

// We will store a 1GB spaceship in here.
const spaceship = new ChangeTracker();

// Simulate a long loading process:
setTimeout(() => {
  spaceship.setValue({ name: 'Friday', color: 'Lilac' });
}, 6174);

// Player gets into a bigger spaceship:
setTimeout(() => {
  spaceship.setValue({ name: 'The Unshakeable', color: 'Blood red' });
}, 9999);

Consumer

The following script wants the variable. It'll ask for it, and proceed the first time that it's set. Note that run order does not matter - this will work whether this code runs first or the spacehip loads first.

/* activateHud.js */

// Receive the value when it's set, then do some processing.
spaceship.getOnce((value) => {
  console.log('Got spaceship:', value);
  // ^-> Got spaceship: { name: 'Friday', color: 'Lilac' }
});

Logger

The above works fine if you care only about getting the current value. Below we have a logger that will keep track of all future changes:

/* logger.js */

// Log the value every time it changes.
spaceship.getEveryChange((value) => {
  const date = new Date().toLocaleString();
  console.log(`[${date}] New spaceship loaded:`, value);
  // ^-> New spaceship loaded: { name: 'Friday', color: 'Lilac' }
  //   > New spaceship loaded: { name: 'The Unshakeable', color: 'Blood red' }
});

Optimising a bit

The above class will notify all getEveryChange listeners every time you set the value, even if it's the exact same value as before. If this is not desired, you can prevent this behaviour by adding if (this._value === value) return; to the setValue class method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM