Search code examples
javascriptyoutube-apiecmascript-6module-patternyoutube-iframe-api

Set variable within callback function with JS module pattern


I am using the YouTube iframe api to load a video that i can then perform actions on.

Im using browserify to create modular behaviour and am using the JS module pattern to break part my code.

When I kick off the createYT function Im using the YouTubeIframeLoader that creates an instance of the YT.Player in its callback.

I would like to assign the YT.Player to a variable that I can access in other functions of my module. Here is that I have so far

const videos = {
   init: function() {
      this.loadYT();
      this.changeVid();
   },
   loadYT: function() {
      YouTubeIframeLoader.load(function(YT) {
        var player = new YT.Player('video-placeholder', {
          height: '390',
          width: '640',
          videoId: 'fObfRDB0JQw'
        });
      });
   },
   changeVid: function() {
       // access player var to perform methods on YT.Player instance
   }
};

export default videos;

Solution

  • I would like to assign the YT.Player to a variable that I can access in other functions of my module.

    The solution to this is quite simple: declare a variable in the outer-most scope of your module and assign it a value within loadYT. For example:

    // Declare `player` here instead
    let player;
    
    const videos = {
       //...
       loadYT: function() {
          YouTubeIframeLoader.load(function(YT) {
            // Assign value to `player`
            player = new YT.Player('video-placeholder', {
              height: '390',
              width: '640',
              videoId: 'fObfRDB0JQw'
            });
          });
       },
       changeVid: function() {
           if (player) {
               // Use `player` here...
           }
           else {
               // Handle `player` not initialised... or throw an Error
               throw new Error('player not initialised');
           }
       }
    };
    
    export default videos;
    

    As @RGraham has mentioned in his comment, you may want to change the behaviour of init such that it returns a Promise which resolves when the callback given to YouTubeIframeLoader.load is invoked. This way you can be sure that initialisation is complete when consuming the remaining APIs (e.g. changeVid) within a then callback.

    let resolve, reject, promise = new Promise((res, rej) => {
        resolve = res;
        reject = rej;
    });    
    
    let initialised = false;
    let player;
    
    const videos = {
       init: function() {
           if (!initialised) this.loadYT();
           return promise;
       },
       loadYT: function() {
          YouTubeIframeLoader.load(function(YT) {
            // Assign value to `player`
            player = new YT.Player('video-placeholder', {
              height: '390',
              width: '640',
              videoId: 'fObfRDB0JQw'
            });
            initialised = true;
            resolve();
          });
          return promise;
       },
       changeVid: function() {
           if (player) {
               // Use `player` here...
           }
           else {
               // Handle `player` not initialised... or throw an Error
               throw new Error('player not initialised');
           }
       }
    };
    
    export default videos;
    

    Example usage:

    videos.init().then(() => {
        videos.changeVid();
    });