waitUntilGeneric_(goalName, p, cleanupOnTimeout, target) {
    let goalMet = false;
    const startTime = Date.now();
    shaka.log.debug('Waiting for ' + goalName);

    // Cache the value of this when we start, in case it changes during the
    // async work below.
    const failOnTimeout = this.failOnTimeout_;

    // The original stacktrace will be lost if we create the Error in the
    // timeout callback below.  Create the Error now so that it has a more
    // useful stacktrace on timeout.
    const error = new Error('Timeout waiting for ' + goalName);

    const success = p.then(() => {
      goalMet = true;
      const endTime = Date.now();
      const seconds = ((endTime - startTime) / 1000).toFixed(2);
      shaka.log.debug(goalName + ' after ' + seconds + ' seconds');
    });

    const timeout = shaka.test.Util.delay(this.timeoutSeconds_).then(() => {
      // Avoid error logs and cleanup callback if we've already met the goal.
      if (goalMet) {
        return;
      }

      cleanupOnTimeout();

      // Improve the error message with media-specific debug info.
      if (target instanceof HTMLMediaElement) {
        this.logDebugInfoForMedia_(error, target);
      }

      // Reject or resolve based on our settings.
      if (failOnTimeout) {
        throw error;
      }
    });

    return Promise.race([success, timeout]);
  }
Wait for a certain Promise to be resolved, or throw on timeout.
Handles all debug logging and timeouts generically.

@param {string} goalName
@param {!Promise} p
@param {function()} cleanupOnTimeout
@param {EventTarget} target
@return {!Promise}
@private