首页 文章

在React.js中执行去抖动

提问于
浏览
331

你如何在React.js中进行去抖动?

我想辩论handleOnChange .

我试过 debounce(this.handleOnChange, 200) 但它不起作用 .

function debounce(fn, delay) {
  var timer = null;
  return function () {
    var context = this, args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function () {
      fn.apply(context, args);
    }, delay);
  };
}
var SearchBox = React.createClass({

    render:function () {
    return (
    <input  type="search" name="p"
       onChange={this.handleOnChange}/>
    );
    },
    handleOnChange: function (event) {
       //make ajax call
    }
});

16 回答

  • 185

    2018年:尝试承诺去债

    我们经常想要去掉api调用,以避免用无用的请求充斥后端 .

    在2018年,仍然使用回调(Lodash / Underscore)感觉不好并且容易出错 . 由于api调用解决了无序问题,很容易遇到样板和并发问题 .

    我已经创建了一个带有React的小库来解决你的痛苦:awesome-debounce-promise

    这不应该比那更复杂:

    const searchAPI = text => fetch('/search?text=' + encodeURIComponent(text));
    
    const searchAPIDebounced = AwesomeDebouncePromise(searchAPI, 500);
    
    class SearchInputAndResults extends React.Component {
      state = {
        text: '',
        results: null,
      };
    
      handleTextChange = async text => {
        this.setState({ text, results: null });
        const result = await searchAPIDebounced(text);
        this.setState({ result });
      };
    }
    

    去抖动功能可确保:

    • api电话将被撤销

    • debounced函数总是返回一个promise

    • 只有最后一次调用的返回承诺才会解决

    • 每个api-call会发生一次 this.setState({ result });

    最后,如果您的组件卸载,您可以添加另一个技巧:

    componentWillUnmount() {
      this.setState = () => {};
    }
    

    请注意, Observables (RxJS)也非常适合去抖动输入,但它是一种更强大的抽象,可能更难以正确学习/使用 .


    还想使用回调去抖动吗?

    这里重要的部分是 to create a single debounced (or throttled) function per component instance . 您不希望多个实例共享相同的去抖功能 .

    我没有真正相关,但是这个答案对于 _.debounce 的下划线或lodash,以及用户提供的去抖动功能都可以完美地运行 .


    不是个好主意:

    var SearchBox = React.createClass({
      method: function() {...},
      debouncedMethod: debounce(this.method,100);
    });
    

    它不起作用,因为在类描述对象创建期间, this 不是自己创建的对象 . this.method 没有返回你期望的内容,因为 this 上下文不是对象本身(它实际上并不存在BTW,因为它刚刚被创建) .


    不是个好主意:

    var SearchBox = React.createClass({
      method: function() {...},
      debouncedMethod: function() {
          var debounced = debounce(this.method,100);
          debounced();
      },
    });
    

    这次你有效地创建了一个调用 this.method 的去抖动函数 . 问题是你在每次 debouncedMethod 呼叫上重新创建它,所以新创建的去抖功能对以前的呼叫一无所知!您必须重复使用相同的去抖功能,否则不会发生去抖动 .


    不是个好主意:

    var SearchBox = React.createClass({
      debouncedMethod: debounce(function () {...},100),
    });
    

    这在这里有点棘手 .

    该类的所有已安装实例将共享相同的去抖功能,并且通常这不是您想要的!请参阅JsFiddle:3个实例仅在全局范围内生成1个日志条目 .

    你必须创建一个去抖动函数 for each component instance ,而不是在每个组件实例共享的类级别的单个去抖动函数 .


    好想法:

    因为debounced函数是有状态的,我们必须创建 one debounced function per component instance .

    ES6 (class property) :推荐

    class SearchBox extends React.Component {
        method = debounce(() => { 
          ...
        });
    }
    

    ES6 (class constructor)

    class SearchBox extends React.Component {
        constructor(props) {
            super(props);
            this.method = debounce(this.method,1000);
        }
        method() { ... }
    }
    

    ES5

    var SearchBox = React.createClass({
        method: function() {...},
        componentWillMount: function() {
           this.method = debounce(this.method,100);
        },
    });
    

    请参阅JsFiddle:3个实例正在为每个实例生成1个日志条目(全局生成3个) .


    照顾React的事件池

    这是相关的,因为我们经常想要去抖动或限制DOM事件 .

    在React中,您在回调中收到的事件对象(即 SyntheticEvent )将被合并(现在为documented) . 这意味着在调用事件回调之后,您收到的SyntheticEvent将被放回具有空属性的池中以减少GC压力 .

    因此,如果您访问原始回调的SyntheticEvent属性异步(如果您节流/去抖动可能就是这种情况),您访问的属性可能会被删除 . 如果您希望永远不会将事件放回池中,则可以使用 persist() 方法 .

    没有持久化(默认行为:池化事件)

    onClick = e => {
      alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
      setTimeout(() => {
        alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
      }, 0);
    };
    

    第二个(异步)将打印 hasNativeEvent=false ,因为事件属性已被清除 .

    坚持不懈

    onClick = e => {
      e.persist();
      alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
      setTimeout(() => {
        alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
      }, 0);
    };
    

    第二个(异步)将打印 hasNativeEvent=true 因为persist()允许避免将事件放回池中 .

    你可以在这里测试这两个行为JsFiddle

    请阅读Julen's answer,了解使用具有油门/去抖功能的 persist() 的示例 .

  • 11

    不受控制的组件

    你可以使用event.persist() method .

    下面使用下划线的 _.debounce() 示例:

    var SearchBox = React.createClass({
    
      componentWillMount: function () {
         this.delayedCallback = _.debounce(function (event) {
           // `event.target` is accessible now
         }, 1000);
      },
    
      onChange: function (event) {
        event.persist();
        this.delayedCallback(event);
      },
    
      render: function () {
        return (
          <input type="search" onChange={this.onChange} />
        );
      }
    
    });
    

    编辑:见this JSFiddle


    受控组件

    更新:上面的示例显示uncontrolled component . 我一直使用受控元素,所以这是上面的另一个例子,但没有使用 event.persist() "trickery" .

    A JSFiddle is available也是如此 . Example without underscore

    var SearchBox = React.createClass({
        getInitialState: function () {
            return {
                query: this.props.query
            };
        },
    
        componentWillMount: function () {
           this.handleSearchDebounced = _.debounce(function () {
               this.props.handleSearch.apply(this, [this.state.query]);
           }, 500);
        },
    
        onChange: function (event) {
          this.setState({query: event.target.value});
          this.handleSearchDebounced();
        },
    
        render: function () {
          return (
            <input type="search"
                   value={this.state.query}
                   onChange={this.onChange} />
          );
        }
    });
    
    
    var Search = React.createClass({
        getInitialState: function () {
            return {
                result: this.props.query
            };
        },
    
        handleSearch: function (query) {
            this.setState({result: query});
        },
    
        render: function () {
          return (
            <div id="search">
              <SearchBox query={this.state.result}
                         handleSearch={this.handleSearch} />
              <p>You searched for: <strong>{this.state.result}</strong></p>
            </div>
          );
        }
    });
    
    React.render(<Search query="Initial query" />, document.body);
    

    编辑:更新的示例和JSFiddles到React 0.12

    编辑:更新示例以解决Sebastien Lorber提出的问题

    编辑:使用jsfiddle更新,不使用下划线并使用普通的javascript去抖动 .

  • 6

    如果您需要从事件对象获取DOM输入元素,那么解决方案就更简单了 - 只需使用 ref

    class Item extends React.Component {
        constructor(props) {
            super(props);
            this.saveTitle = _.throttle(this.saveTitle.bind(this), 1000);
        }
        saveTitle(){
            let val = this.inputTitle.value;
            // make the ajax call
        }
        render() {
            return <input 
                        ref={ el => this.inputTitle = el } 
                        type="text" 
                        defaultValue={this.props.title} 
                        onChange={this.saveTitle} />
        }
    }
    
  • 3

    我发现Justin Tulk的this post非常有帮助 . 经过几次尝试之后,人们会认为是反应/减少更正式的方式,它表明它由于React's synthetic event pooling而失败 . 然后他的解决方案使用一些内部状态来跟踪 Value 在输入中更改/输入,在 setState 之后立即进行回调,该回调调用一个实时显示某些结果的限制/去抖动redux操作 .

    import React, {Component} from 'react'
    import TextField from 'material-ui/TextField'
    import { debounce } from 'lodash'
    
    class TableSearch extends Component {
    
      constructor(props){
        super(props)
    
        this.state = {
            value: props.value
        }
    
        this.changeSearch = debounce(this.props.changeSearch, 250)
      }
    
      handleChange = (e) => {
        const val = e.target.value
    
        this.setState({ value: val }, () => {
          this.changeSearch(val)
        })
      }
    
      render() {
    
        return (
            <TextField
                className = {styles.field}
                onChange = {this.handleChange}
                value = {this.props.value}
            />
        )
      }
    }
    
  • 5

    经过一段时间的文本输入挣扎而没有找到我自己的完美解决方案后,我发现这个在npm https://www.npmjs.com/package/react-debounce-input

    这是一个简单的例子:

    import React from 'react';
    import ReactDOM from 'react-dom';
    import {DebounceInput} from 'react-debounce-input';
    
    class App extends React.Component {
    state = {
        value: ''
    };
    
    render() {
        return (
        <div>
            <DebounceInput
            minLength={2}
            debounceTimeout={300}
            onChange={event => this.setState({value: event.target.value})} />
    
            <p>Value: {this.state.value}</p>
        </div>
        );
    }
    }
    
    const appRoot = document.createElement('div');
    document.body.appendChild(appRoot);
    ReactDOM.render(<App />, appRoot);
    

    DebounceInput组件接受您可以分配给普通输入元素的所有道具 . 试试codepen

    我希望它也可以帮助别人并节省一些时间 .

  • 0

    使用ES6 CLASS和 React 15.x.x &lodash.debounce我在这里使用React的 refs ,因为事件在内部丢失了这个绑定 .

    class UserInput extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          userInput: ""
        };
        this.updateInput = _.debounce(this.updateInput, 500);
      }
    
    
      updateInput(userInput) {
        this.setState({
          userInput
        });
        //OrderActions.updateValue(userInput);//do some server stuff
      }
    
    
      render() {
        return ( <div>
          <p> User typed: {
            this.state.userInput
          } </p>
          <input ref = "userValue" onChange = {() => this.updateInput(this.refs.userValue.value) } type = "text" / >
          </div>
        );
      }
    }
    
    ReactDOM.render( <
      UserInput / > ,
      document.getElementById('root')
    );
    
    <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.5/lodash.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    
    
    <div id="root"></div>
    
  • 2

    如果您使用redux,您可以使用中间件以非常优雅的方式执行此操作 . 您可以将 Debounce 中间件定义为:

    var timeout;
    export default store => next => action => {
      const { meta = {} } = action;
      if(meta.debounce){
        clearTimeout(timeout);
        timeout = setTimeout(() => {
          next(action)
        }, meta.debounce)
      }else{
        next(action)
      }
    }
    

    然后,您可以向操作创建者添加去抖动,例如:

    export default debouncedAction = (payload) => ({
      type : 'DEBOUNCED_ACTION',
      payload : payload,
      meta : {debounce : 300}
    }
    

    实际上already middleware你可以下午npm为你做这件事 .

  • 1

    你可以使用Lodash去抖https://lodash.com/docs/4.17.5#debounce方法 . 它简单而有效 .

    import * as lodash from lodash;
    
    const update = (input) => {
        // Update the input here.
        console.log(`Input ${input}`);     
    }
    
    const debounceHandleUpdate = lodash.debounce((input) => update(input), 200, {maxWait: 200});
    
    doHandleChange() {
       debounceHandleUpdate(input);
    }
    

    您也可以使用以下方法取消去抖动方法 .

    this.debounceHandleUpdate.cancel();
    

    希望它能帮到你 . 干杯!!

  • -1

    这里有很多好的信息,但要简洁 . 这对我有用......

    import React, {Component} from 'react';
    import _ from 'lodash';
    
    class MyComponent extends Component{
          constructor(props){
            super(props);
            this.handleChange = _.debounce(this.handleChange.bind(this),700);
          };
    
  • 1

    这是我想出的一个例子,用辩护者包装另一个类 . 这非常适合制作装饰器/高阶函数:

    export class DebouncedThingy extends React.Component {
        static ToDebounce = ['someProp', 'someProp2'];
        constructor(props) {
            super(props);
            this.state = {};
        }
        // On prop maybe changed
        componentWillReceiveProps = (nextProps) => {
            this.debouncedSetState();
        };
        // Before initial render
        componentWillMount = () => {
            // Set state then debounce it from here on out (consider using _.throttle)
            this.debouncedSetState();
            this.debouncedSetState = _.debounce(this.debouncedSetState, 300);
        };
        debouncedSetState = () => {
            this.setState(_.pick(this.props, DebouncedThingy.ToDebounce));
        };
        render() {
            const restOfProps = _.omit(this.props, DebouncedThingy.ToDebounce);
            return <Thingy {...restOfProps} {...this.state} />
        }
    }
    
  • 1

    而不是将handleOnChange包装在debounce()中,为什么不将ajax调用包装在debounce内部的回调函数内,从而不会破坏事件对象 . 所以这样的事情:

    handleOnChange: function (event) {
       debounce(
         $.ajax({})
      , 250);
    }
    
  • 3

    我正在寻找同一个问题的解决方案并且遇到了这个线程以及其他一些但是他们遇到了同样的问题:如果你想做一个 handleOnChange 函数并且你需要一个事件目标的值,你将得到 cannot read property value of null 或者一些这样的错误 . 在我的情况下,我还需要在debounced函数中保留 this 的上下文,因为我是我的解决方案,它适用于我的用例所以我将它留在这里以防万一有人遇到这个帖子:

    // at top of file:
    var myAction = require('../actions/someAction');
    
    // inside React.createClass({...});
    
    handleOnChange: function (event) {
        var value = event.target.value;
        var doAction = _.curry(this.context.executeAction, 2);
    
        // only one parameter gets passed into the curried function,
        // so the function passed as the first parameter to _.curry()
        // will not be executed until the second parameter is passed
        // which happens in the next function that is wrapped in _.debounce()
        debouncedOnChange(doAction(myAction), value);
    },
    
    debouncedOnChange: _.debounce(function(action, value) {
        action(value);
    }, 300)
    
  • -6

    对于 throttledebounce ,最好的方法是创建一个函数创建器,以便您可以在任何地方使用它,例如:

    updateUserProfileField(fieldName) {
        const handler = throttle(value => {
          console.log(fieldName, value);
        }, 400);
        return evt => handler(evt.target.value.trim());
      }
    

    在您的 render 方法中,您可以:

    <input onChange={this.updateUserProfileField("givenName").bind(this)}/>
    

    updateUserProfileField 方法每次调用时都会创建一个单独的函数 .

    Note 不要尝试直接返回处理程序,例如这不起作用:

    updateUserProfileField(fieldName) {
        return evt => throttle(value => {
          console.log(fieldName, value);
        }, 400)(evt.target.value.trim());
      }
    

    之所以这不起作用,是因为每次调用该事件而不是使用相同的油门功能时都会产生一个新的油门功能,所以油门基本没用;)

    此外,如果您使用 debouncethrottle ,您不需要 setTimeoutclearTimeout ,这实际上是我们使用它们的原因:P

  • 657

    Julen解决方案有点难以阅读,对于任何根据 Headers 而不是问题的微小细节而绊倒他的人来说,这里的代码更清晰,更具针对性 .

    tl;dr version :当您更新到观察者时,发送调用调度方法而不是反过来将实际通知观察者(或执行ajax等)

    用示例组件完成jsfiddle http://jsfiddle.net/7L655p5L/4/

    var InputField = React.createClass({
    
        getDefaultProps: function () {
            return {
                initialValue: '',
                onChange: null
            };
        },
    
        getInitialState: function () {
            return {
                value: this.props.initialValue
            };
        },
    
        render: function () {
            var state = this.state;
            return (
                <input type="text"
                       value={state.value}
                       onChange={this.onVolatileChange} />
            );
        },
    
        onVolatileChange: function (event) {
            this.setState({ 
                value: event.target.value 
            });
    
            this.scheduleChange();
        },
    
        scheduleChange: _.debounce(function () {
            this.onChange();
        }, 250),
    
        onChange: function () {
            var props = this.props;
            if (props.onChange != null) {
                props.onChange.call(this, this.state.value)
            }
        },
    
    });
    
  • 3

    你也可以使用自编的mixin,如下所示:

    var DebounceMixin = {
      debounce: function(func, time, immediate) {
        var timeout = this.debouncedTimeout;
        if (!timeout) {
          if (immediate) func();
          this.debouncedTimeout = setTimeout(function() {
            if (!immediate) func();
            this.debouncedTimeout = void 0;
          }.bind(this), time);
        }
      }
    };
    

    然后在你的组件中使用它,如下所示:

    var MyComponent = React.createClass({
      mixins: [DebounceMixin],
      handleClick: function(e) {
        this.debounce(function() {
          this.setState({
            buttonClicked: true
          });
        }.bind(this), 500, true);
      },
      render: function() {
        return (
          <button onClick={this.handleClick}></button>
        );
      }
    });
    
  • 5

    一个人不需要大量的局部变量来获得合适的油门功能 . 节流功能的目的是减少浏览器资源,而不是应用你正在使用的更多开销 . 作为这种说法的证据的证明,我设计了一种油门功能,其范围内只有4个"hanging"变量 . ("hanging"变量是一个永远不会被垃圾收集的变量,因为它总是被一个可能被调用的函数引用,从而吸收内存 . )少数油门功能通常不会造成任何伤害;但是,如果有数千个节流功能,那么如果使用非常低效的油门功能,内存就会变得稀缺 . 我的解决方案如下 .

    var timenow=self.performance ? performance.now.bind(performance) : Date.now;
    function throttle(func, alternateFunc, minInterval) {
        var lastTimeWent = -minInterval, freshArguments=null;
        function executeLater(){
            func.apply(null, freshArguments);
            freshArguments = null;
            lastTimeWent = 0;
        }
        return function() {
            var newTimeWent = timenow();
            if ((newTimeWent-lastTimeWent) > minInterval) {
                lastTimeWent = newTimeWent;
                return func.apply(null, arguments);
            } else {
                if (freshArguments === null)
                    setTimeout(executeLater, minInterval-(newTimeWent-lastTimeWent));
                freshArguments = arguments;
                if (typeof alternateFunc === "function")
                    return alternateFunc.apply(null, arguments);
            }
        };
    }
    

    然后,围绕EventTarget包装此限制函数,例如DOM点击,窗口事件,XMLHttpRequests onprogress,FileReader onprogress,[等],如下所示:

    var tfCache = []; // throttled functions cache
    function listen(source, eventName, func, _opts){
        var i = 0, Len = tfCache.length, cF = null, options = _opts || {};
        a: {
            for (; i < Len; i += 4)
                if (tfCache[i] === func &&
                  tfCache[i+1] === (options.ALTERNATE||null) &&
                  tfCache[i+2] === (options.INTERVAL||200)
                ) break a;
            cF = throttle(func, options.ALTERNATE||null, options.INTERVAL||200);
            tfCache.push(func, options.ALTERNATE||null, options.INTERVAL||200, cF);
        }
        source.addEventListener(eventName, cF || tfCache[i+3], _opts);
        return cF === null; // return whether it used the cache or not
    };
    function mute(source, eventName, func, _opts){
        var options = _opts || {};
        for (var i = 0, Len = tfCache.length; i < Len; i += 4)
            if (tfCache[i] === func &&
              tfCache[i+1] === (options.ALTERNATE||null) &&
              tfCache[i+2] === (options.INTERVAL||200)
            ) {
                source.removeEventListener(eventName, tfCache[i+3], options);
                return true;
            }
        return false;
    }
    

    下面是一个按钮的示例,该按钮被限制为每秒仅侦听一次点击 . 当它收到此单击时,它将更改为新的随机颜色 .

    (function(){"use strict";
    // The function throttler //
    var timenow=self.performance ? performance.now.bind(performance) : Date.now;
    function throttle(func, alternateFunc, minInterval) {
        var lastTimeWent = -minInterval, freshArguments=null;
        function executeLater(){
            func.apply(null, freshArguments);
            freshArguments = null;
            lastTimeWent = 0;
        }
        return function() {
            var newTimeWent = timenow();
            if ((newTimeWent-lastTimeWent) > minInterval) {
                lastTimeWent = newTimeWent;
                return func.apply(null, arguments);
            } else {
                if (freshArguments === null)
                    setTimeout(executeLater,minInterval-(newTimeWent-lastTimeWent));
                freshArguments = arguments;
                if (typeof alternateFunc === "function")
                    return alternateFunc.apply(this, arguments);
            }
        };
    }
    // The EventTarget wrapper: //
    var tfCache = []; // throttled functions cache
    function listen(source, eventName, func, _opts){
        var i = 0, Len = tfCache.length, cF = null, options = _opts || {};
        a: {
            for (; i < Len; i += 4)
                if (tfCache[i] === func &&
                  tfCache[i+1] === (options.ALTERNATE||null) &&
                  tfCache[i+2] === (options.INTERVAL||200)
                ) break a;
            cF = throttle(func, options.ALTERNATE||null, options.INTERVAL||200);
            tfCache.push(func, options.ALTERNATE||null, options.INTERVAL||200, cF);
        }
        source.addEventListener(eventName, cF || tfCache[i+3], _opts);
        return cF === null; // return whether it used the cache or not
    };
    function mute(source, eventName, func, _opts){
        var options = _opts || {};
        for (var i = 0, Len = tfCache.length; i < Len; i += 4)
            if (tfCache[i] === func &&
              tfCache[i+1] === (options.ALTERNATE||null) &&
              tfCache[i+2] === (options.INTERVAL||200)
            ) {
                source.removeEventListener(eventName, tfCache[i+3], options);
                return true;
            }
        return false;
    }
    // Finally, the color changing button: //
    function randHex(){ // weighted towards the ends of the scales for contrast
        var rand = Math.random()*2 - 1; // equally offcenter it from one
        var sign = rand < 0 ? -1 : 1; // get a sign-ish value
        rand = Math.sqrt(rand * sign) * sign; // stretch it further from zero
        rand = 128 + rand * 128; // next, recenter it to range from 0 to 255 
        var str = (rand | 0).toString(16); // make an integer string
        while (str.length < 2) str = "0" + str; // pad it
        return str; // finally, return it
    }
    var clickerEle = document.getElementById("clicker");
    var dropperEle = document.getElementById("droppedClicks");
    var deDs = dropperEle.dataset; // deDs = droperEle DataSet
    var dropSkips = 0;
    function whenClick(){
        if (dropSkips > 10) { // if the user clicked fast enough
            mute(clickerEle, "click", whenClick, theOptions);
            dropperEle.textContent = "You won with " + dropSkips + 
                " clicks per second! The button no longer changes color";
        }
        dropSkips = 0;
        deDs ? delete deDs.numdrops : dropperEle.removeAttribute("data-numdrops");
        clickerEle.setAttribute("style", "background:#"+randHex()+randHex()+randHex());
    }
    var theOptions = {
        ALTERNATE: function(){
            // whenever the main function is skipped:
            deDs.numdrops = dropSkips += 1;
        },
        INTERVAL: 2000,
        passive: true
    };
    listen(clickerEle, "click", whenClick, theOptions);
    whenClick(); // to start changing the color
    document.body.addEventListener("contextmenu", function(x){x.preventDefault()});
    })();
    
    #droppedClicks[data-numdrops]::before {
        content: "Dropped " attr(data-numdrops) " clicks";
        color: green;
    }
    
    Click the button below as fast as you can! You win when you are able to click the button more than ten times in a single second ().

    <button id="clicker"><h3>Click me</h3></button> <div id="droppedClicks"></div>

    保存这个答案之后,我发现有毒SO社区的持久性downvotes阻止了这个片段的运行 . 因此,这是一个JSFiddle的链接:https://jsfiddle.net/t7ymkzLx/2/

    默认情况下,此功能限制为每200ms最多一次调用 . 要将间隔更改为不同的毫秒数,请在options参数中传递 optionsObject.INTERVAL 选项,并将其设置为执行之间所需的最小毫秒数 . (因为计时器并不总是最多的精确的,)如果你有一个确切的最小间隔,那么我建议你从所需的 optionsObject.INTERVAL 中减去一两个,以确保它总是至少应该被执行 . 如果在受限制的函数执行被延迟时(由于过多的调用),您需要对限制的func的参数执行某些操作,请使用 optionsObject.ALTERNATE 选项 . 这个"ALTERNATE"是一个函数,只要删除对主函数的调用,就会立即调用它来代替主函数 . 例如,如果您在EventTarget上使用限制函数,但想要在已删除事件上使用 preventDefault() ,则使用 {ALTERNATE: function(evt){ evt.preventDefault(); }} 作为选项对象 .

相关问题