【问题标题】:How to set a JS variable depending on the duration of an asynchronous call如何根据异步调用的持续时间设置 JS 变量
【发布时间】:2020-12-11 18:23:14
【问题描述】:

我有这个:

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

首先触发加载微调器。 然后查询运行。 当查询函数结束时,加载微调器会关闭。

但是如果我只想显示加载微调器,而查询时间可能超过 0.5 秒呢?

有没有简单的方法来做到这一点?

【问题讨论】:

    标签: javascript async-await laravel-livewire alpine.js


    【解决方案1】:

    实现此目的的一种方法是将this.query(term) 承诺传递给一个函数,该函数将仅在查询花费的时间超过指定时间量(使用超时)时处理触发toggleWaiting

    例如,下面接受一个承诺,一个函数 (waitingFn) 将使用isWaiting 状态调用以及一个timeout,您可以使用它来指定您希望在您之前等待多长时间显示加载微调器。最后,当 Promise 实现时,我们返回结果:

    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 所指出的(谢谢你们),由于使用了promise 构造函数,下面是一个反模式:

    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);
           });
      });
    }
    

    【讨论】:

    【解决方案2】:

    感谢 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/7c1f8270e107a97b03264f5ddc5c3c3ae6f7cfd7

    【讨论】:

    • 此代码(以及 ljbc1994 的)具有 Explicit Promise construction antipattern!您应该改用链接和承诺组合...此外,返回承诺但不等待任何内容的函数不必是async
    • 好的。听起来不错。我喜欢编写干净的代码。谢谢你。您能否给我一个小代码示例,如何使用 promise 组合而不使用 async 标志来编写它?
    • 嘿,我已经修改了答案以删除反模式
    • @Slowwie 我想在空闲时间写一个答案,但我很高兴you've already got an answer ;)
    猜你喜欢
    • 1970-01-01
    • 2023-02-16
    • 1970-01-01
    • 2012-10-09
    • 1970-01-01
    • 2023-01-12
    • 2014-05-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多