【问题标题】:wait until condition is met or timeout is passed in javascript等到满足条件或在javascript中传递超时
【发布时间】:2018-08-09 19:40:23
【问题描述】:

我需要让代码休眠,直到满足某些条件或超过 3 秒超时。然后返回一个简单的字符串。无论如何我可以做到这一点吗?

// this function needs to return a simple string 

function something() { 

    var conditionOk = false;

    var jobWillBeDoneInNMiliseconds = Math.floor(Math.random() * 10000);

    setTimeout(function() {

        // I need to do something here, but I don't know how long it takes
        conditionOk = true; 

    }, jobWillBeDoneInNMiliseconds);


    // I need to stop right here until
    // stop here until ( 3000 timeout is passed ) or ( conditionOk == true )
    StopHereUntil( conditionOk, 3000 );

    return "returned something"; 
}

这就是我要做的:

我让浏览器滚动到页面底部,然后会调用一些 ajax 函数来获取 cmets(我无法控制它)。现在我需要等到 cmets 出现在带有“.comment”类的文档中。

我需要 getComments() 函数将 cmets 作为 json 字符串返回。

function getComments() {

    window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);

  var a = (document.querySelectorAll('div.comment'))

  // wait here until  (  a.length > 0  ) or ( 3 second is passed )

  // then I need to collect comments
  var comments = [];
  document.querySelectorAll('div.comment p')
    .forEach(function(el){      
        comments.push(el.text());
    });

  return JSON.stringify(comments);
} 

getComments();

【问题讨论】:

  • 这还不够信息:什么情况会导致OKfalse?这不是一个太微不足道的任务,通常使用Observables / EventEmitters 解决此类问题
  • 我在代码中创建了一个简单的示例。例如。 condition 变量将在大约 1 到 9 秒内为真。我需要代码等待 3 秒,直到该变量更改为 true
  • 由于 JavaScript 被执行 asynchronously 我需要查看代码 sn-p 评估它是否成功。也许是ajax 请求?没有更多信息无法回答。
  • 你不能真正从一个将处理移交给异步函数的函数返回一些东西。听起来你可能想使用类似 Promise 的东西 developers.google.com/web/fundamentals/primers/promises
  • 请查看并告诉我这是否是您的问题? jsfiddle.net/qxw54mzs/16

标签: javascript sleep


【解决方案1】:

我遇到了这个问题,没有一个解决方案令人满意。我需要等到某个元素出现在 DOM 中。所以我接受了hedgehog125的回答并根据我的需要进行了改进。我认为这回答了最初的问题。

async function sleepUntil(f, timeoutMs) {
    return new Promise((resolve, reject) => {
        let timeWas = new Date();
        let wait = setInterval(function() {
            if (f()) {
                console.log("resolved after", new Date() - timeWas, "ms");
                clearInterval(wait);
                resolve();
            } else if (new Date() - timeWas > timeoutMs) { // Timeout
                console.log("rejected after", new Date() - timeWas, "ms");
                clearInterval(wait);
                reject();
            }
        }, 20);
    });
}

用法:

await sleepUntil(() => document.querySelector('.my-selector'), 5000);

【讨论】:

  • 变量 timeWas 以及 wait 在您的示例中未声明
  • @CodingYourLife 回想起来,我不知道为什么我没有在函数范围内正确声明变量。我现在将编辑答案。感谢您的关注。
【解决方案2】:

您应该能够使用Promise.race 实现此目的。这是一个基本示例:

let promise1 = new Promise(resolve => {
  setTimeout(resolve, 500, 'one');
});
let promise2 = new Promise(resolve => {
  setTimeout(resolve, 800, 'two');
});

async function fetchAndLogResult() {
  let result = await Promise.race([promise1, promise2]);
  console.log(result);
}

fetchAndLogResult();

这是一个替代版本,虽然没有使用async/await,但更简洁:

let promise1 = new Promise(resolve => {
  setTimeout(resolve, 500, 'one');
});
let promise2 = new Promise(resolve => {
  setTimeout(resolve, 800, 'two');
});

Promise.race([promise1, promise2]).then(result => console.log(result));

【讨论】:

  • 在任何相对现代的浏览器上都可以。否则,您可以使用 Babel 之类的工具转换为跨浏览器等效项。
  • 对不起。 Promise.race 返回一个 Promise { <pending> } 对象。我需要得到一个字符串。
  • @mdaliyan 这就是您使用await 的原因。检查 sn -p:result 是一个字符串,它显示在控制台上。唯一的要求是您正在执行此操作的函数标记为async。不过,我会用不同的版本来做同样的事情,也许会有所帮助。
  • @mdaliyan 好了,帖子已编辑。所以你基本上需要做的就是用你的实际任务替换承诺(在任务完成时调用resolve 与结果),例如new Promise(resolve => resolve(performTaskAndReturnResult()))
  • 我无法返回Promise。我什至不能使用Promise.resolve()。该函数需要返回简单的string
【解决方案3】:

在 JavaScript 中没有等待的方法。您可以使用 settimeout 或使用 while 循环(请记住,在这种情况下脚本无法运行,然后页面可能会变得无响应)。

设置超时

// this function needs to return a simple string 

function something() { 

    conditionOk = false;

    var jobWillBeDoneInNMiliseconds = Math.floor(Math.random() * 10000);

    timeout = setTimeout(function() {

        // I need to do something here, but I don't know how long it takes
        conditionOk = true; 

    }, jobWillBeDoneInNMiliseconds);


    // I need to stop right here until
    // stop here until ( 3000 timeout is passed ) or ( conditionOk ==     true )

    timeWas = new Date();

    wait = setInterval(function() {
        if (conditionOk) {
            // Communicate what you were trying to return using globals
            clearInterval(wait);
        }
        if (new Date() - timeWas > 3000) { // Timeout
            // Clear this interval
            clearInterval(wait);
        }
    }, 30);
}

同时

// this function needs to return a simple string 

function something() { 

    conditionOk = false;

    var jobWillBeDoneInNMiliseconds = Math.floor(Math.random() * 10000);

    timeout = setTimeout(function() {

        // I need to do something here, but I don't know how long it takes
        conditionOk = true; 

    }, jobWillBeDoneInNMiliseconds);


    // I need to stop right here until
    // stop here until ( 3000 timeout is passed ) or ( conditionOk ==     true )

    timeWas = new Date();

    while ((! conditionOk) && (! (new Date() - timeWas > 3000))) { // 3000 = the delay
        // Do nothing
    }
    if (conditionOk) {
        return "returned something";
    }
    else {
        return "returned nothing";
    }
}

您可能还想看看这个问题:JavaScript sleep/wait before continuing

希望这会有所帮助!

【讨论】:

  • 我不能使用setTimeout。函数需要返回结果
  • 第二个(with while)代码冻结并且不返回任何内容。
  • 哎呀。我现在已经修好了。
  • 看起来没问题,但是如果你把timeout从3000改成5000,再把10000改成300,代码还是要等5秒!!!很奇怪
  • while循环无法获取conditionOk的更新值。见jsfiddle.net/mdaomega/0u6kobom/1
【解决方案4】:

当我在寻找类似的解决方案时,我在浏览器中打开了这个问题。可能帖子作者不再需要它了,但这里有来自非事件循环世界(php、java、python)的其他人。

这是我在阅读 MDN 后得到的: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

这两个描述了需要什么。为此,您将需要一些辅助函数。它看起来臃肿,但似乎在 JS 中没有其他方法。虽然完成了工作:)

// sleep helper
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
}

// func that will check your condition
function checkCondition(number) {
    if (number == 5) return true;
    else return false;
}

// helper that will wait and then check the condition
// await halts the execution flow until sleep is resolved
// this is the part that you need
async function waitOneSecondAndCheckCondition(number) {
    const v = await sleep(500);
    return checkCondition(number);
}

// since most likely you want to poll the checkCondition
// and not waste all the time waiting
// this polls conditon every half a second
// and in the end return a simple string
// either when condition is met or after three seconds
async function waitUntilConditionIsMetOrTimeoutIsPassed() {
    for (let i = 0; i < 6; i++) {
        let result = await waitOneSecondAndCheckCondition(i);
        console.log("i is: " + i + " condition result is: " + result);
        if (!result) continue;
        else break;
    }
}

waitUntilConditionIsMetOrTimeoutIsPassed();

如果在 3 秒内的某个时间点满足条件,则控制台输出:

i is: 0 condition result is: false
i is: 1 condition result is: false
i is: 2 condition result is: false
i is: 3 condition result is: true
a simple string

发生超时时的控制台输出:

i is: 0 condition result is: false
i is: 1 condition result is: false
i is: 2 condition result is: false
i is: 3 condition result is: false
i is: 4 condition result is: false
i is: 5 condition result is: false
a simple string

希望这对你们所有像我一样的 JS 新手有帮助 :)

【讨论】:

    【解决方案5】:

    如果你想等到一个条件满足:

    main();
    
    async function main() {
      let foo = 0;
      // for demo purposes, artificially increment foo
      setInterval(() => foo++);
      console.log('Waiting until foo reaches 1337 ...');
      await until(() => foo === 1337);
      console.log('foo === ' + foo);
    }
    
    function until(condition) {
      return new Promise((resolve) => {
        const interval = setInterval(() => {
          if (condition()) {
            clearInterval(interval);
            resolve();
          }
        });
      });
    }

    如果你想等到一定时间过去:

    main();
    
    async function main() {
      console.log('Waiting 2 seconds ...');
      await milliseconds(2_000);
      console.log('Done!');
    }
    
    function milliseconds(ms) {
      return new Promise((resolve) => setTimeout(resolve, ms));
    }

    【讨论】:

      【解决方案6】:

      好的,因为您使用的是ajax,您可以执行以下操作:

      var eventEmitter = new EventEmitter()
      eventEmitter.on('myEvent', myFunction)
      
      $.ajax(...).then(function() {
        eventEmitter.emit('myEvent', {state: true})
      })
      
      setTimeout(function() { eventEmitter.emit('myEvent', {state: false}), 3000);
      
      function myFunction() {
         //you can do your checks inside here
      }
      

      你的 ajax 不使用 jQuery:

      var xhr = new XMLHttpRequest();
      xhr.open('GET', 'myservice/username?id=some-unique-id');
      xhr.onload = function() {
        if (xhr.status === 200) {
          eventEmitter.emit('myEvent', {state: true})
        }
        else {
          alert('Request failed.  Returned status of ' + xhr.status);
        }
      };
      xhr.send();
      

      【讨论】:

      • 我不是发送 ajax 请求的人。我只能滚动到页面底部并等待获取 cmets。我已经编辑了我的问题。见第二个代码。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-05-02
      • 2012-01-10
      • 2020-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多