【问题标题】:How to run a function inside a event once?如何在事件中运行一次函数?
【发布时间】:2021-03-12 07:48:51
【问题描述】:

我在 Angular.js 中有一个函数来确定用户是处于非活动状态还是处于活动状态。当用户处于活动状态时,我想在一段时间后执行一个功能。

我让它工作了,但该函数当前运行的数量与执行的事件相同。 如何保证这个函数每次执行一次?

.run(function($timeout, $document) {
    // console.log('starting run');

    // Timeout timer value
    var TimeOutTimerValue = 120000;

    // Start a timeout
    var TimeOutThread = $timeout(function() {
        LogoutByTimer();
    }, TimeOutTimerValue);
    var bodyElement = angular.element($document);

    angular.forEach(
        [
            'keydown',
            'keyup',
            'click',
            'mousemove',
            'DOMMouseScroll',
            'mousewheel',
            'mousedown',
            'touchstart',
            'touchmove',
            'scroll',
            'focus'
        ],
        function(EventName) {
            bodyElement.bind(EventName, function(e) {
                TimeOutResetter(e);
            });
        }
    );

    function LogoutByTimer() {
        console.log('Logout');
    }

    function TimeOutResetter(e) {
        console.log(' ' + e);

        $timeout(function() {
            console.log('run this once ');
        }, 2000);

        // Stop the pending timeout
        $timeout.cancel(TimeOutThread);

        // Reset the timeout
        TimeOutThread = $timeout(function() {
            LogoutByTimer();
        }, TimeOutTimerValue);
    }
})

这是关于函数TimeOutResetter(e) 中的console.log('run this once')。 如何运行一次?

Click here to see it on Plunker

【问题讨论】:

  • 我认为简单的方法是创建一个值为 false 的全局 var,然后为您想要运行 1 次的部分创建一个 if 并查看是否为 false 并运行它,当您运行它时将 var 更改为true 所以下次不符合条件
  • 是的,看起来不错,让我试试。
  • @SimoneRossaini no 没用,试过var runOnce = false; $timeout(function() { if (!runOnce) { console.log('send session'); } runOnce = true; }, 2000);
  • 如果您想为源代码中的多个函数实现相同的行为(仅运行一次),您可能希望将行为抽象为包装函数,使您能够“一次化”任何函数-the-fly,比如myFunctionOnce = once(myFunction)。请参阅:davidwalsh.name/javascript-once

标签: javascript angularjs dom-events


【解决方案1】:

我会在外部范围内添加一个带有超时句柄的变量。然后,当您即将开始超时时,您检查是否已定义此句柄。如果没有,它就会运行。

这是一个例子:

app.run(function($rootScope, $timeout, $document) {    
    console.log('starting run');

    // Declare the handle
    var executeOnceTimeout;

    ...

    function TimeOut_Resetter(e){
        console.log(' ' + e);

        // Check if handle exists, if not continue.
        if (!executeOnceTimeout) {
            // Assign timeout-function to variable. 
            executeOnceTimeout = $timeout(function() {
                console.log('call this once');
            }, 2000);
        }
        ...
    }

})

更新

根据 cmets,听起来您正在寻找的是将“callOnce”函数限制一段时间,这实际上意味着它只能每 X 毫秒调用一次。

上面的代码可以通过以下更改来实现:

// Check if handle exists, if not continue.
if (!executeOnceTimeout) {
    // Assign timeout-function to variable. 
    executeOnceTimeout = $timeout(function() {
        // Do work
        console.log('call this once');
        // Reset callback, to enable it to be called again next time an event happens. 
        executeOnceTimeout = null;
    }, 2000);
}

You can see it in action using this Plunker.

【讨论】:

  • 这会执行一次控制台日志,但是当事件再次发生时需要再次执行。现在它只执行一次,但需要在每个事件之后重复。
  • 您希望它何时重置?您可以在需要重置时简单地重新分配变量executeOnceTimeout = null,它会在下次调用TimeOut_Resetter-函数时再次运行。
  • 我想要做的是,当你有 47 [object MouseEvent] 时,带有日志的函数会显示一次,然后你有另一个事件 17 [object MouseEvent],在这个事件之后我想显示日志再一次,所以在每个事件之后 1x 日志/函数。
  • 您可以在 callOnce-function 的末尾添加executeOnceTimeout = null。这样做的是,每次 callOnce 函数完成时,它都会重置。实际上,这意味着 callOnce 函数最多每 2000 毫秒被调用一次。这种效果称为节流。这就是你想要的吗?
  • 不客气。我已更新答案以更好地反映您的意图。
【解决方案2】:

我用你的 plunk 和工作试了一下,看我的例子:

<!DOCTYPE html>
<html ng-app="Application_TimeOut">
<head>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script>
</head>

<body>
</body>

<script>
var app = angular.module('Application_TimeOut', []);
app.run(function($rootScope, $timeout, $document) {    
    console.log('starting run');
    var runOnce = true;
    // Timeout timer value
    var TimeOutTimerValue = 5000;

    // Start a timeout
    var TimeOut_Thread = $timeout(function(){ LogoutByTimer() } , TimeOutTimerValue);
    var bodyElement = angular.element($document);
    
    angular.forEach(['keydown', 'keyup', 'click', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mousedown', 'touchstart', 'touchmove', 'scroll', 'focus'], 
    function(EventName) {
         bodyElement.bind(EventName, function (e) { TimeOut_Resetter(e) });  
    });

    function LogoutByTimer(){
        console.log('Logout');
        ///////////////////////////////////////////////////
        /// redirect to another page(eg. Login.html) here
        ///////////////////////////////////////////////////
    }

    function TimeOut_Resetter(e){
        console.log(' ' + e);
        if(runOnce){
            runOnce = false;
            $timeout(function() {
                console.log('run this once');               
            }, 2000);            
        }
        
        /// Stop the pending timeout
        $timeout.cancel(TimeOut_Thread);

        /// Reset the timeout
        TimeOut_Thread = $timeout(function(){ LogoutByTimer() } , TimeOutTimerValue);
    }

})
</script>
</html>

我在顶部添加 var runOnce = true; 然后在运行命令之前添加 if 并在运行时更改它。

【讨论】:

  • 这会执行一次控制台日志,但是当事件再次发生时需要再次执行。现在它只执行一次,当事件再次发生时它会停止执行,但需要在每个事件之后重复一次。
  • 很简单,注销时添加true。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-13
  • 1970-01-01
  • 2021-06-29
  • 2021-11-22
  • 1970-01-01
  • 2012-12-03
相关资源
最近更新 更多