clearInterval, когда на объект больше не ссылаются?

У меня есть класс, который действует как клиент для сервера (через WebSocket). Я хотел бы реализовать систему, которая периодически пингует сервер для определения задержки. Тем не менее, я обеспокоен тем, что если я использую setInterval для этого внутри класса он будет продолжать пытаться пропинговать после того, как объект должен быть собран мусором. Как я могу узнать, когда позвонить clearInterval?

Резюме кода:

class WSClient extends EventEmitter
{
    private latency: number;
    public get Latency(): number
    { return this.latency; }

    public async ping(): Promise<number>
    { ... }

    public constructor(options)
    {
        super();

        // Do constructor stuff

        setInterval(() => this.ping().then(latency => this.latency = latency), 1000);
    }
}

2 ответа

Вы можете использовать setInterval() и сохранить его в переменной, затем вы можете получить доступ к этому интервалу следующим образом:

class WSClient extends EventEmitter
{
    private latency: number;
    public get Latency(): number
    { return this.latency; }

    public async ping(): Promise<number>
    { ... }

    public constructor(options)
    {
        super();

        // Do constructor stuff

        this.interval = setInterval(() => this.ping()
        .then(latency => this.latency = latency), 1000);
    }
}

Тогда, когда вам нужно:

WSClient.interval.clearInterval();

Вот в чем дело: вы никогда не достигнете точки, где объект "должен" быть собран мусором, потому что setInterval вы определили, что содержит ссылку на этот объект (в вашем контексте, как this) навечно. Вам понадобится дополнительная логика, чтобы определить, нужно ли вам ее запускать.

То, что я бы порекомендовал, и это простой подход, так как вы уже определили get Latency(), заключается в том, чтобы ввести некоторую логику, чтобы отслеживать, действительно ли кто-то действительно спрашивал время ожидания. Если геттер был запущен недавно, продолжайте опрос. Если это не так, удалите интервал.

Вы могли бы сделать это намного проще, если бы вы определили, вместо этого, async getLatency()Таким образом, если вы обнаружите, что задержка не была недавно проверена, вы можете подождать, пока задержка не будет пересчитана.

Я не запускал это, но включаю это, чтобы проиллюстрировать идею:

// ms to wait until cancelling the interval
const latencyTimeout = 200000;

// In your class

async getLatency(): number {
  if (!this.latency) {
    const started = Date.now();
    const poller = setInterval(async () => {
       if (Date.now() - started > latencyTimeout) {
         clearInterval(poller);
         this.latency = null;
       }
       this.latency = await this.ping();
    }, 1000);
    this.latency = await this.ping();
  }
  return this.latency;
}

Кроме того, вы можете рассмотреть возможность не использовать setInterval, но повторяющийся setTimeout вместо. Проблема с интервалом в том, что он основан на собственных часах. Он не будет учитывать количество времени, необходимое для завершения пинга. Например, если вы опрашиваете каждую секунду, но для завершения пинга требуется 500 мсек, все будет в порядке, но если пинг потребует 2000 мсек, тогда ваши пинги фактически выйдут из строя. Может показаться, что у вас гораздо более медленный пинг, потому что вы получаете отдачу от пинга, который занял больше времени, чем недавний, который работал быстро. Лучше сделать setTimeout вместо этого он запускается только после завершения последнего.

Другие вопросы по тегам