Как установить переменную JS в зависимости от длительности асинхронного вызова

У меня есть это:

this.toggleWaiting()
this.results = await this.query(term)
this.toggleWaiting()

Сначала срабатывает загрузчик. Затем выполняется запрос. И когда функция запроса закончилась, загрузчик закрывается.

Но что, если я хочу просто показать счетчик загрузки, когда запрос занимает, может быть, более 0,5 секунды?

Есть ли простой способ сделать это?


person Slowwie    schedule 11.12.2020    source источник


Ответы (2)


Способ добиться этого — передать обещание this.query(term) функции, которая будет обрабатывать запуск toggleWaiting только тогда, когда запрос занимает больше времени, чем указанное количество времени (используя тайм-аут).

Например, в приведенном ниже примере используется обещание, функция (waitingFn), которая будет вызываться со статусом isWaiting, а также timeout, которую вы можете использовать, чтобы указать, как долго вы хотите ждать, прежде чем показывать счетчик загрузки. Наконец, когда обещание выполнено, мы возвращаем результат:

async function handleWaiting(promise, waitingFn, timeout) {
  let loadingStarted = false;
  let timeoutInstance = null;

  const timeoutPromise = new Promise((res) => {
    timeoutInstance = setTimeout(() => {
      loadingStarted = true;
      waitingFn(true);
    }, timeout);
    return res();
  });

  function onFinished() {
    clearTimeout(timeoutInstance);

    if (loadingStarted) {
      waitingFn(false);
    }
  }

  try {
    const [result] = await Promise.all([promise, timeoutPromise]);
    onFinished();
    return result;
  } catch (ex) {
    onFinished();
    throw ex;
  }
}

Вы можете вызвать функцию handleWaiting следующим образом:

const result = await handleWaiting(this.query(term), (isWaiting) => this.toggleWaiting(), 500);

Как отметили @FZs и @Bergi (спасибо вам обоим), ниже приведен антипаттерн из-за использования конструктора обещаний:

function handleWaiting(promise, waitingFn, timeout) {
  return new Promise((res, rej) => {
     let loadingStarted = false;
   
     const timeoutInstance = setTimeout(() => {
       loadingStarted = true; 
       waitingFn(true);
     }, timeout);

     function onFinished() {
       if (loadingStarted) {
         waitingFn(false);
       }
       clearTimeout(timeoutInstance);
     }
     
     return promise
       .then((result) => {
         onFinished();
         res(result);
       })
       .catch((ex) => {
         onFinished();
         rej(ex);
       });
  });
}
person ljbc1994    schedule 11.12.2020
comment
Этот код имеет антишаблон конструкции Explicit Promise! Вместо этого вы должны использовать цепочку и композицию обещаний... Кроме того, функция, которая возвращает обещание, но ничего не ожидает, не обязательно должна быть async - person FZs; 12.12.2020
comment
Избегайте антишаблона конструктора Promise конструктора! - person Bergi; 12.12.2020

Благодаря ljbc1994 я нашел хорошее решение.

Внутри моего объекта alpineJs у меня есть эта реализация:

{
   waiting: false,

   async handleWaiting(promise, timeout) {
       return new Promise((res, rej) => {
           let loadingStarted = false;

           const timeoutInstance = setTimeout(() => {
               loadingStarted = true;
               this.waiting = true;
           }, timeout);

           const onFinished = () => {
               if (loadingStarted) {
                   this.waiting = false;
               }
               clearTimeout(timeoutInstance);
           }

           promise
               .then((result) => {
                   onFinished();
                   res(result);
               })
               .catch((ex) => {
                   onFinished();
                   rej(ex);
               });
       });
    },

    async searchForTerm(term) {
       this.results = await this.handleWaiting(this.$wire.query(term), 500);
       // do something with the results...
    },
 }

Довольно просто.

Для тех, кому интересен полный код — вот коммит внутри репозитория github:

https://github.com/MichaelBrauner/sunfire-form/commit/7c1f8270e1077fc3fcdd63fc03264 >

person Slowwie    schedule 12.12.2020
comment
Этот код (как и код ljbc1994) имеет антишаблон конструкции Explicit Promise! Вместо этого вы должны использовать цепочку и композицию обещаний... Кроме того, функция, которая возвращает обещание, но ничего не ожидает, не обязательно должна быть async - person FZs; 12.12.2020
comment
Ok. Звучит отлично. Мне нравится писать чистый код. Спасибо. Не могли бы вы дать мне небольшой пример кода, как написать его с композицией промисов и без асинхронного флага? - person Slowwie; 12.12.2020
comment
Эй, я изменил ответ, чтобы удалить антипаттерн - person ljbc1994; 12.12.2020
comment
@Slowwie Я хотел написать ответ на это, когда у меня было свободное время, но я рад, что вы уже получили ответ< /а> ;) - person FZs; 12.12.2020