简体   繁体   English

等到满足条件或在javascript中传递超时

[英]wait until condition is met or timeout is passed in javascript

I need to sleep the code until some condition is met or a 3 second timeout is passed.我需要让代码休眠,直到满足某些条件或通过 3 秒超时。 then return a simple string.然后返回一个简单的字符串。 Is there anyway I can do this?无论如何我可以做到这一点吗?

// 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"; 
}

here is what I exactly going to do:这就是我要做的:

I make the browser scroll to bottom of the page, then some ajax function will be called to fetch the comments (that I have not control on it).我让浏览器滚动到页面底部,然后将调用一些 ajax 函数来获取评论(我无法控制它)。 Now I need to wait until comments are appeared in document with ".comment" class.现在我需要等到评论出现在带有“.comment”类的文档中。

I need the getComments() function return comments as a json string.我需要getComments()函数将评论作为 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();

I came across this problem and none of the solutions were satisfactory.我遇到了这个问题,没有一个解决方案令人满意。 I needed to wait until a certain element appeared in the DOM.我需要等到某个元素出现在 DOM 中。 So I took hedgehog125's answer and improved it to my needs.所以我接受了hedgehog125的回答并根据我的需要进行了改进。 I think this answers the original question.我认为这回答了最初的问题。

const sleepUntil = async (f, timeoutMs) => {
    return new Promise((resolve, reject) => {
        const timeWas = new Date();
        const 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);
    });
}

Usage (async/await promise):用法(异步/等待承诺):

try {
    await sleepUntil(() => document.querySelector('.my-selector'), 5000);
    // ready
} catch {
    // timeout
}

Usage (.then promise):用法(.then 承诺):

sleepUntil(() => document.querySelector('.my-selector'), 5000)
    .then(() => {
        // ready
    }).catch(() => {
        // timeout
    });

You should be able to achieve this using Promise.race .您应该能够使用Promise.race来实现这一点。 Here's a basic example:这是一个基本示例:

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

Here's an alternative version, more concise although not using async / await :这是一个替代版本,虽然不使用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));

In JavaScript there isn't a way to wait.在 JavaScript 中,没有办法等待。 You can either use settimeout or you can use a while loop (bare in mind that scripts can't run while this is happening and then the page could become unresponsive).您可以使用settimeout也可以使用 while 循环(请记住,在这种情况下脚本无法运行,然后页面可能会变得无响应)。

With settimeout设置超时

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

With 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();

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

You might also want to look at this question: JavaScript sleep/wait before continuing您可能还想看看这个问题: JavaScript sleep/wait before continue

Hope this helps!希望这可以帮助!

I had this question opened in my browser while I was looking for a similar solution.当我在寻找类似的解决方案时,我在浏览器中打开了这个问题。 Probably post author doesn't need it anymore, but here goes to others coming from non event-looped worlds (php, java, python).可能帖子作者不再需要它,但这里有来自非事件循环世界(php、java、python)的其他人。

Here's what I got after reading 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这是我在阅读 MDN 后得到的: https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await https://developer.mozilla.org/en-US/docs/Web /JavaScript/参考/语句/async_function

These two describe what is needed.这两个描述了需要什么。 You'll need some helper functions for that.为此,您将需要一些辅助函数。 It looks bloated, but it seems there's no other way around in JS.它看起来臃肿,但似乎在 JS 中没有其他方法。 Gets the job done though :)虽然完成了工作:)

 // 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();

console output if condition is met at some point within 3s:如果在 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

Console output when a timeout occurred:发生超时时的控制台输出:

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

Hope this helps all of you JS newcomers as me :)希望这对你们所有像我一样的JS新手有帮助:)

If you want to wait until a condition is met:如果要等到满足条件:

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

If you want to wait until a certain amount of time has passed:如果你想等到一定时间过去:

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

Ok because you are using ajax you can do the following:好的,因为您使用的是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
}

your ajax without using jQuery :不使用jQuery的 ajax:

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

For Typescript, I've modified @pbotas answer like this:对于 Typescript,我修改了 @pbotas 的答案,如下所示:

export const sleepUntil = async (f: () => boolean, timeoutMs: number) => {
  return new Promise((resolve, reject) => {
    const timeWas = new Date();
    const wait = setInterval(function () {
      if (f()) {
        console.log('resolved after', +new Date() - +timeWas, 'ms');
        clearInterval(wait);
        resolve(true);
      } else if (+new Date() - +timeWas > timeoutMs) {
        // Timeout
        console.log('rejected after', +new Date() - +timeWas, 'ms');
        clearInterval(wait);
        reject(false);
      }
    }, 20);
  });
};

Notice that I'm evaluation a boolean condition.请注意,我正在评估一个布尔条件。

For invoking this function:调用此函数:

sleepUntil(() => someBoolean, 5000)
 .then(() => {
    // ready
 })
 .catch(() => {
   // timeout
 });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM