【问题标题】:ES6 promises with timeout intervalES6 承诺超时间隔
【发布时间】:2018-04-10 18:08:52
【问题描述】:

我正在尝试将我的一些代码转换为 Promise,但我不知道如何在 Promise 中链接新的 Promise。

我的 promise 函数应该每隔一秒左右检查一次数组的内容,如果里面有任何项目应该解析。否则它应该等待 1s 并再次检查,依此类推。

function get(){
    return new Promise((resolve) => {

      if(c.length > 0){
        resolve(c.shift());

      }else{
        setTimeout(get.bind(this), 1000);
      }

    });

}


let c = [];

setTimeout(function(){
  c.push('test');
}, 2000);

这就是我希望我的 get() 承诺函数工作的方式,它应该在最多 2 或 3 秒后打印“test”:

get().then((value) => {
  console.log(value);
});

显然它不起作用,什么都没有打印出来

【问题讨论】:

    标签: javascript node.js promise


    【解决方案1】:

    使用setInterval 每秒检查一次。运行此脚本即可理解。

    let c = [];
    
    function get(){
        return new Promise((resolve) => {
    
          var i = setInterval(function(){
            if(c.length > 0){
              resolve(c.shift());
              clearInterval(i);
            }
          }, 1000);
    
        });
    }
    
    
    
    setTimeout(function(){
      c.push('test');
    }, 2000);
    
    get().then((value) => {
      console.log(value);
    });

    【讨论】:

    • 您还应该提供一种在解决间隔后删除间隔的方法。否则,回调将无限运行。
    【解决方案2】:

    问题是你的递归调用没有传递resolve 函数,所以else 分支永远不能调用resolve

    解决此问题的一种方法是在 Promise 的回调中创建一个闭包,以便递归调用可以访问与对 get 的初始调用相同的 resolve 变量。

    function get() {
      return new Promise((resolve) => {
        function loop() {
          if (c.length > 0) {
            resolve(c.shift());
          } else {
            setTimeout(loop, 1000);
          }
        }
        loop();
      });
    }
    
    let c = [];
    setTimeout(function() {
      c.push('test');
    }, 2000);
    get().then(val => console.log(val));

    【讨论】:

    • c 不为空会怎样?我认为他想要的东西不可能从一个 Promise 中实现。
    • @Mark_M 如果c 不为空,它将立即resolve 使用c 中的第一个值。
    • 我读到这个问题的方式是他想在那之后继续检查。
    • @Mark_M 问题的哪一部分让你这么认为? Promise 只能解析一次,因此任何后续对 resolve 的调用都将被忽略。
    • 是的,我明白,这就是我认为不可能的原因。我想我以这种方式阅读它的原因是几乎没有理由通过所有这些来只返回一个值......以及'......等等'。我当然可能看错了。
    【解决方案3】:

    else 的情况下,您永远不会解决 那个 承诺。 get 可能会创建另一个,但它会返回到无处。

    你应该 promisify 你的异步函数 (setTimeout) 在最低级别,然后只链接你的承诺。通过returning 来自then 回调的递归调用的结果,生成的promise 将以相同的结果解析:

    function delayAsync(time) {
        return new Promise(resolve => {
            setTimeout(resolve, time);
        });
    }
    function get(c) {
        if (c.length > 0){
            return Promise.resolve(c.shift());
        } else {
            return delay(1000).then(() => {
                return get(c); // try again
            });
        }
    }
    

    【讨论】:

      【解决方案4】:

      您需要的是一项轮询服务,它会定期检查特定条件,然后再进行承诺解决。目前,当您运行 setTimeout(get.bind(this), 1000); 时,您正在创建一个新的 Promise 实例,而实际上并未解析初始 Promise,因为您没有引用您创建的初始 resolve 函数。

      解决办法:

      • 创建一个新的回调函数,你可以在 Promise 中引用它
      • 在 setTimeout 调用中将 resolvereject 作为参数传递,例如setTimeout(HandlePromise, 1000, resolve, reject, param3, param4 ..);setTimeout API

      function get() {
        var handlerFunction = resolve => {
          if (c.length > 0) {
            resolve(c.shift());
          } else {
            setTimeout(handlerFunction, 1000, resolve);
          }
        };
      
        return new Promise(handlerFunction);
      }
      
      let c = [];
      
      setTimeout(function() {
        c.push("test");
      }, 2000);
      
      get().then(value => {
        console.log(value);
      });

      【讨论】:

        【解决方案5】:

        setTimeout 本身具有糟糕的链接和错误处理特性,所以总是wrap it

        const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
        
        function get(c) {
          if (c.length) {
            return Promise.resolve(c.shift());
          }
          return wait(1000).then(() => get(c)); // try again
        }
        
        let c = [];
        get(c).then(val => console.log(val));
        wait(2000).then(() => c.push('test'));

        虽然你没有问,但为了他人的利益,这是 async/await 闪耀的绝佳案例:

        const wait = ms => new Promise(r => setTimeout(r, ms));
        
        async function get(c) {
          while (!c.length) {
            await wait(1000);
          }
          return c.shift();
        }
        
        let c = [];
        get(c).then(val => console.log(val));
        wait(2000).then(() => c.push('test'));

        注意这次我们不需要Promise.resolve(),因为async 函数会隐式执行此操作。

        【讨论】:

          【解决方案6】:

          你可以试试这个解决方案。由于 JS 需要释放自己来下载图像,我在异步函数中使用 await 并在延迟后使用异步调用来唤醒 JS

          private async onBeforeDoingSomething() : void {
              await this.delay(1000);
              console.log("All images are loaded");    
          } 
          
          private delay (ms : number = 500) : Promise<number> {
              return new Promise((resolve,reject) => {
                  const t = setTimeout( () => this.areImgsLoaded(resolve), ms);
              });
          } 
          
          private async areImgsLoaded (resolve) {
              let reload = false;
              const img = document.querySelectorAll('img');
              console.log("total of images: ",img.length);
          
              for (let i = 0; i < img.length; i++){
                  if (!img[i]["complete"]) {
                      console.log("img not load yet");
                      reload = true;
                      break;
                  }
              }
          
              if (reload) {
                  await this.delay();
              }
              resolve();
          }
          

          【讨论】:

            猜你喜欢
            • 2015-03-14
            • 1970-01-01
            • 2016-11-20
            • 2018-11-28
            • 2016-06-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-09-04
            相关资源
            最近更新 更多