Background:

  • Thinking about the event loop mechanism triggered by event stabilization, partially asynchronously placed in the execution order outside the closure
  • Code combat: React event handler onClick = call function “this.fn()” directly, Bind this “this.fn.bind(this)” when called, or bind this “() => this.handleclick () when called with the arrow function

Say first conclusion

In buffeting, part of the asynchronous closure is placed outside the closure, resulting in the following result:

.// Some asynchronous parts are not shaken
    _this.props.dispatch({
        type: "test/debounce",
        param
    })
    .then(() = > {
        _this.editData()
    })
    // Part of the asynchronous end is not buffered
    .then(() = > {
        return function () {
            console.log('-- first timeout: ', timeout)  2 / / 2
            clearTimeout(timeout);  // Every time the input value is triggered, the previous setTimeout is cleared
            console.log('-- timeout: ', timeout);  / / 3. 3
            timeout = setTimeout(() = > { / / 4 4
                    _this.updateAge()   // Retrieve page data again
                console.log('-- I'm setTimeout, anti-shake cleanup functions'."-- timeout value:", timeout)
            }, 1000) // Note that if you write code that refers to this, you need to declare who this is, because there is no reference to this in the arrow function
            console.log('-- Closure in debounce completes execution ', timeout)
        }()
    })
    ...
    
    
Copy the code
  • The synchronization code is executed first (i.e. after two clicks), and setTimeout is executed last.
  • This. UpdateAge is not cleared because setTimeout is not defined when clearTimeout is executed
  • The final number of times setTimeout is executed is the number of clicks

Author: Lin Xuan Link: juejin.cn/post/698919… The copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please indicate the source.

Way to write — The correct way to write

Step 1: Define the click method in Render

  • Option 1 in the code below: not feasible;

Because bind is a function that returns a change to this, it will change debounce this and return it as it is;

And what we’re going to do is debounce() and to do that, we’re going to have onClick = debounce(), which returns a closure, and ultimately what we’re going to do is we’re going to execute the function that returns debounce

  • Option 2 in the code below: Yes;

Debounce (), which returns the closure defined inside;

Render will execute debounce(), but it doesn’t really matter, we’re mainly executing the function returned from debounce(), and debounce doesn’t have too much logic in front of its definition to go wrong

// render
/ / way
{/* - */}  

2 / / way
<span className={styles.left} onClick={this.debounce('subtract')}>-</span> 

Copy the code

Step 2: Define the Debounce event

  • Method 1: Write all functions inside setTimeout, ok

To use closures properly, a cleanup function is one that clears all functions, not the outside – the Debounce definition in example 2

debounce = (option) = > {
    console.log('-- Enter the debounce method ')
    let param={};
    // Handle some logic
    if (option === 'str') {
        param = {a:1}}else {
        param = {a:1}}const _this = this;
    let timeout = null;
    return function () {
        console.log('-- first timeout: ', timeout)
        clearTimeout(timeout);  
        console.log('-- timeout: ', timeout)
        timeout = setTimeout(() = > { / / 4 4Arunachal Pradesh// The buffering function **
            _this.props.dispatch({
                type: "test/debounce",
                param
            })
            .then(() = > {
                _this.editData()
            })
            .then(() = > {
                _this.updateAge()
            })
            **// End **

            console.log('-- I'm setTimeout, anti-shake cleanup functions'."-- timeout value:", timeout)
        }, 500) 
        console.log('-- Closure in debounce completes execution ', timeout)
    }
}
Copy the code
  • Below is the execution sequence when the button is clicked twice quickly:

SetTimeout clears all functions and only executes the last request, so only prints ‘~~~ I am requesting ‘once.

The variable value of setTimeout is a number that uniquely identifies setTimeout

2 – Incorrect writing (write part of the closure asynchronously, just want to clear some function jitter)

First step: Render definition

  • Because of the error of writing the second method, which is to write part of the asynchronous closure outside, just want to clear part of the function jitter,

  • Render, the asynchrons in debounce() will be executed, and perhaps some data will be updated. Render may result in an error

  • Bind (), which changes this, returns a function body instead of executing the function; Instead of causing debounce to execute on render, debounce will be executed on onClick

  • Can only write the following, two kinds of writing, the two see writing difference: react.docschina.org/docs/handli…

<span className={styles.left} onClick={this.debounce.bind(this.'subtract')}>-</span> 
Copy the code

Step 2: Define debounce

  • Write part of the asynchronous closure outside, do not tremble; I just want to clear some function jitter,

  • See the following figure for the execution results, and see “conclusion” below for reasons.

// Add or subtract ages
debounce = (option) = > {
    console.log('-- Enter the debounce method ')
    let param={};
    // Handle some logic
    if (option === 'str') {
        param = {a:1}}else {
        param = {a:1}}const _this = this;
    let timeout = null;

    // Some asynchronous parts are not shaken
    _this.props.dispatch({
        type: "test/debounce",
        param
    })
    .then(() = > {
        _this.editData()
    })
    // Part of the asynchronous end is not buffered
    .then(() = > {
        return function () {
            console.log('-- first timeout: ', timeout)  2 / / 2
            clearTimeout(timeout);
            console.log('-- timeout: ', timeout);  / / 3. 3
            timeout = setTimeout(() = > { / / 4 4
                _this.updateAge()
                console.log('-- I'm setTimeout, anti-shake cleanup functions'."-- timeout value:", timeout)
            }, 1000)
            console.log('-- Closure in debounce completes execution ', timeout)
        }()
    })

}
Copy the code

The reason why the execution result is incorrectly written

Reference source: segmentfault.com/a/119000002…

  • Because the event loop mechanism is the primary task – > micro task – > macro task, setTimeout is the macro task, will need to execute the next time period

  • The flow chart of event cycle mechanism is as follows:

  • Details are as follows:

Each time the main thread is executed, check whether it is a synchronous task or an asynchronous API

3. The asynchronous thread executes the asynchronous API. 4. The asynchronous thread executes the asynchronous API. The asynchronous callback event is put into the event queue. After the synchronization task in the hand of the main thread is finished, the main thread comes to the event queue to see if there is any task. The main thread finds that there is a task in the event queue, and then it takes out the task and executes the above process continuously

Wrong timer

In the Event Loop process, there are some hidden holes. The most typical problem is that the synchronization task is always executed first, and then the callback in the Event queue is executed. This feature directly affects the execution of the timer. Let’s think about the process we started with the 2-second timer:

The main thread executes the synchronization code

2 met setTimeout and gave it to the timer thread 3 timer thread start timing, 2 seconds to the notification event trigger thread 4 events trigger thread put the timer callback in queue, it is the end of 5 main thread asynchronous processes if have time, will take out the timer callback execution, if not the callback has been in the queue.

conclusion

The execution results of the above incorrect writing method are as follows:

  • The synchronization code is executed first (i.e. after two clicks), and setTimeout is executed last.

  • This. UpdateAge is not cleared because setTimeout is not defined when clearTimeout is executed

  • The final number of times setTimeout is executed is the number of clicks

Here’s an example: Same principle

<! DOCTYPE html><html>

<head>
    <meta charset="utf-8">
    <title></title>
</head>

<body>
    <div></div>
    <input id="inp" />
    <script>
        inp.addEventListener('input'.() = > {
            console.log('script start');
            setTimeout(function () {
                console.log('---setTimeout')},5000);

            Promise.resolve().then(function () {
                console.log('promise1');
            }).then(function () {
                console.log('promise2'); })});</script>
</body>

</html>
Copy the code

Print the following: