【问题标题】:Wait until a condition is true?等到某个条件成立?
【发布时间】:2011-11-03 19:52:02
【问题描述】:

我在 JavaScript 中使用 navigator.geolocation.watchPosition,我想要一种方法来处理用户可能在 watchPosition 找到其位置之前提交依赖位置的表单的可能性。

理想情况下,用户会定期看到“等待位置”消息,直到获得位置,然后提交表单。

但是,由于缺少 wait 函数,我不确定如何在 JavaScript 中实现它。

当前代码:

var current_latlng = null;
function gpsSuccess(pos){
    //console.log('gpsSuccess');  
    if (pos.coords) { 
        lat = pos.coords.latitude;
        lng = pos.coords.longitude;
    }
    else {
        lat = pos.latitude;
        lng = pos.longitude;
    }
    current_latlng = new google.maps.LatLng(lat, lng);
}
watchId = navigator.geolocation.watchPosition(gpsSuccess,
                  gpsFail, {timeout:5000, maximumAge: 300000});
$('#route-form').submit(function(event) {
    // User submits form, we need their location...
    while(current_location==null) {
        toastMessage('Waiting for your location...');
        wait(500); // What should I use instead?
    }
    // Continue with location found...
});

【问题讨论】:

  • 异步思考。查找setTimeout
  • 异步和递归可能 - 递归调用 setTimeout 直到 current_latlng 有一些价值?
  • 在指责它之前先了解语言。肯定不会“缺少”wait 函数。
  • @Richard 是的,但无论你做什么都使用超时。当以任何方式(几乎所有)连续运行时,Javascript 将使用多少 CPU 资源,您会感到惊讶。
  • @Richard:没错。由于异步性,它不是严格的函数调用递归,但从代码的角度来看,确实如此。

标签: javascript geolocation


【解决方案1】:

就我个人而言,我使用了一个waitfor() 函数,它封装了一个setTimeout()

//**********************************************************************
// function waitfor - Wait until a condition is met
//        
// Needed parameters:
//    test: function that returns a value
//    expectedValue: the value of the test function we are waiting for
//    msec: delay between the calls to test
//    callback: function to execute when the condition is met
// Parameters for debugging:
//    count: used to count the loops
//    source: a string to specify an ID, a message, etc
//**********************************************************************
function waitfor(test, expectedValue, msec, count, source, callback) {
    // Check if condition met. If not, re-check later (msec).
    while (test() !== expectedValue) {
        count++;
        setTimeout(function() {
            waitfor(test, expectedValue, msec, count, source, callback);
        }, msec);
        return;
    }
    // Condition finally met. callback() can be executed.
    console.log(source + ': ' + test() + ', expected: ' + expectedValue + ', ' + count + ' loops.');
    callback();
}

我以下列方式使用我的waitfor() 函数:

var _TIMEOUT = 50; // waitfor test rate [msec]
var bBusy = true;  // Busy flag (will be changed somewhere else in the code)
...
// Test a flag
function _isBusy() {
    return bBusy;
}
...

// Wait until idle (busy must be false)
waitfor(_isBusy, false, _TIMEOUT, 0, 'play->busy false', function() {
    alert('The show can resume !');
});

【讨论】:

  • 不要忘记waitfor()函数末尾的);
  • 但是如果在最后的(也就是初始的)waitfor 调用下面还有更多的代码,那么代码不会继续执行,直到它暂停,因为是时候运行第一个预定的 setTimeout 调用了?最初 waitfor 只是一个设置 setTimeout 调用然后返回的函数。您的代码将观察直到值改变,但它不会阻塞直到值改变。
【解决方案2】:

使用 Promise 的现代解决方案

function waitFor(conditionFunction) {

  const poll = resolve => {
    if(conditionFunction()) resolve();
    else setTimeout(_ => poll(resolve), 400);
  }

  return new Promise(poll);
}

用法

waitFor(_ => flag === true)
  .then(_ => console.log('the wait is over!'));

async function demo() {
  await waitFor(_ => flag === true);
  console.log('the wait is over!');
}

参考文献
Promises
Arrow Functions
Async/Await

【讨论】:

    【解决方案3】:

    你会想要使用setTimeout:

    function checkAndSubmit(form) {
        var location = getLocation();
        if (!location) {
            setTimeout(checkAndSubmit, 500, form); // setTimeout(func, timeMS, params...)
        } else {
            // Set location on form here if it isn't in getLocation()
            form.submit();
        }
    }
    

    ...getLocation 在其中查找您的位置。

    【讨论】:

    • 您也可以在gpsFail 中添加标志,然后在checkAndSubmit 中检查并显示正确的消息。
    【解决方案4】:

    这正是 Promise 被发明和实现的目的(因为 OP 问了他的问题)。

    查看所有不同的实现,例如 promisejs.org

    【讨论】:

    • 这是这个问题唯一正确的答案,到现在还是0票真的很让人担心
    • @HansWesterbeek 可能是因为承诺的概念是一个广泛的主题。我已经有一种应该使用承诺的感觉,但是这个答案太模糊了,无法帮助我。
    【解决方案5】:

    您可以使用超时尝试重新提交表单:

    $('#route-form').submit(function(event) {
        // User submits form, we need their location...
        if(current_location==null) {
            toastMessage('Waiting for your location...');
            setTimeout(function(){ $('#route-form').submit(); }, 500); // Try to submit form after timeout
            return false;
        } else {
            // Continue with location found...
        }
    });
    

    【讨论】:

    • 必须是字符串,否则超时会得到.submit的值。我想你可以做 function(){ return $('#route-form').submit(); }。我认为这会奏效。
    • 我完全听不懂你的第一句话。而且,是的,函数表达式是正确的方法。 :)
    • 可能是过度优化,但不是function(){ $('#route-form').submit(); },你不能做$('#route-form').submit吗?
    • 是的,你也可以这样做,可能会更干净一些。
    【解决方案6】:
    class App extends React.Component {
    
    componentDidMount() {
       this.processToken();
    
    }
     processToken = () => {
        try {
            const params = querySearch(this.props.location.search);
            if('accessToken' in params){
                this.setOrderContext(params);
                this.props.history.push(`/myinfo`);
            }
        } catch(ex) {
          console.log(ex);
        }
        }
    
       setOrderContext (params){
         //this action calls a reducer and put the token in session storage
         this.props.userActions.processUserToken({data: {accessToken:params.accessToken}});
    }
    
    render() {
        return (
    
            <Switch>
                //myinfo component needs accessToken to retrieve my info
                <Route path="/myInfo" component={InofUI.App} />
            </Switch>
    
        );
    }
    

    然后在 InofUI.App 内部

    componentDidMount() {
            this.retrieveMyInfo();
        }
    
        retrieveMyInfo = async () => {
            await this.untilTokenIsSet();
            const { location, history } = this.props;
            this.props.processUser(location, history);
        }
    
        untilTokenIsSet= () => {
            const poll = (resolve) => {
                const { user } = this.props;
                const { accessToken } = user;
                console.log('getting accessToken', accessToken);
                if (accessToken) {
                    resolve();
                } else {
                    console.log('wating for token .. ');
                    setTimeout(() => poll(resolve), 100);
                }
            };
            return new Promise(poll);
        } 
    

    【讨论】:

      【解决方案7】:

      尝试像这样使用setIntervalclearInterval...

      var current_latlng = null;
      
      function gpsSuccess(pos) {
          //console.log('gpsSuccess');  
          if (pos.coords) {
              lat = pos.coords.latitude;
              lng = pos.coords.longitude;
          } else {
              lat = pos.latitude;
              lng = pos.longitude;
          }
          current_latlng = new google.maps.LatLng(lat, lng);
      }
      watchId = navigator.geolocation.watchPosition(gpsSuccess,
          gpsFail, {
              timeout: 5000,
              maximumAge: 300000
          });
      $('#route-form').submit(function (event) {
          // User submits form, we need their location...
          // Checks status every half-second
          var watch = setInterval(task, 500)
      
          function task() {
              if (current_latlng != null) {
                  clearInterval(watch)
                  watch = false
                  return callback()
              } else {
                  toastMessage('Waiting for your location...');
      
              }
          }
      
          function callback() {
              // Continue on with location found...
          }
      });
      

      【讨论】:

        【解决方案8】:
        export default (condition: Function, interval = 1000) =>
          new Promise((resolve) => {
            const runner = () => {
              const timeout = setTimeout(runner, interval);
        
              if (condition()) {
                clearTimeout(timeout);
                resolve(undefined);
                return;
              }
            };
        
            runner();
          });
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-11-24
          • 2016-07-19
          • 1970-01-01
          • 2018-01-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多