async attempt() {
    if (this.numAttempts_ >= this.maxAttempts_) {
      if (this.autoReset_) {
        this.reset_();
      } else {
        throw new shaka.util.Error(
            shaka.util.Error.Severity.CRITICAL,
            shaka.util.Error.Category.PLAYER,
            shaka.util.Error.Code.ATTEMPTS_EXHAUSTED);
      }
    }

    const currentAttempt = this.numAttempts_;
    this.numAttempts_++;

    if (currentAttempt == 0) {
      goog.asserts.assert(!this.autoReset_, 'Failed to delay with auto-reset!');
      return;
    }

    // We've already tried before, so delay the Promise.

    // Fuzz the delay to avoid tons of clients hitting the server at once
    // after it recovers from whatever is causing it to fail.
    const fuzzedDelayMs = shaka.net.Backoff.fuzz_(
        this.nextUnfuzzedDelay_, this.fuzzFactor_);

    await new Promise((resolve) => {
      shaka.net.Backoff.defer(fuzzedDelayMs, resolve);
    });

    // Update delay_ for next time.
    this.nextUnfuzzedDelay_ *= this.backoffFactor_;
  }
@return {!Promise} Resolves when the caller may make an attempt, possibly
  after a delay.  Rejects if no more attempts are allowed.