As a developer who transfers data from other programming languages (C # Java) to Javascript, setTimeout () the running principle of the method is a difficult part to understand. This article attempts to combine the implementation of other programming languages, from setTimeout, the event loop model is used as a developer who transfers data from other programming languages (C #/Java) to Javascript. When learning Javascript, setTimeout () the running principle of the method is a poor understanding I have encountered. This article attempts to use the implementation of other programming languages to describe the event loop model from setTimeout.
1. Start with setTimeout
The setTimeout () method is not defined in the ecmascript specification, but a function provided by BOM. View w3school's definition of the setTimeout () method. The setTimeout () method is used to call a function or computing expression after a specified number of milliseconds.
Syntax setTimeout (fn, millisec). fn indicates the code to be executed. It can be a string containing javascript code or a function. The second parameter millisec is represented by milliseconds, indicating how long the fn execution will be postponed.
After the setTimeout () method is called, The method returns a number, which is the unique identifier of the code to be executed and can be used to cancel the timeout call.
At first, I used setTimeout () in a relatively simple way and did not have a deep understanding of its running mechanism until I saw the following code:
var start = new Date;setTimeout(function(){var end = new Date;console.log('Time elapsed:', end - start, 'ms');}, 500);while (new Date - start < 1000) {};
In my first understanding of setTimeout (), the latency is set to 500 ms, so the output should be Time elapsed: 500 ms. In an intuitive understanding, the Javascript execution engine should execute the above Code sequentially from top to bottom. The setTimeout function is executed before the while statement. However, after the code is run multiple times, the output latency is at least 1000 ms.
2. Java implementation of setTimeout
I am confused by the setTimeout () of the preceding Javascript, which reminds me of my previous Java learning experience. Java implements multiple APIs for setTimeout. Here we use the java. util. Timer package as an example. Use Timer to implement the above logic in Java and run it multiple times. The output is Time elapsed: 501 ms.
import java.util.Date;import java.util.Timer;import java.util.TimerTask; public class TimerTest { public static void main(String[] args) { // TODO Auto-generated method stub long start = System.currentTimeMillis(); Timer timer = new Timer(); timer.schedule(new MyTask(start), 500); while (System.currentTimeMillis() - start < 1000) {}; } } class MyTask extends TimerTask { private long t; public MyTask(long start) { // TODO Auto-generated constructor stub t=start; } @Override public void run() { // TODO Auto-generated method stub long end = System.currentTimeMillis(); System.out.println("Time elapsed:"+(end - this.t)+ "ms"); } }
Let's take a closer look at the difference between setTimeout () and java. util. Timer.
The key elements of the above Code are the schedule method of the Timer, TimerTask, and Timer classes. You can understand its implementation by reading the relevant source code.
Timer: The scheduling class of a Task. Similar to TimerTask, TimerTask is an API class for users to use. The schedule method is used to schedule the Task execution plan. This class schedules tasks through TaskQueue Task queue and TimerThread.
TimerTask: implements the Runnable interface, indicating that each task is an independent thread. You can use the run () method to customize your own tasks.
TimerThread: inherits from Thread and is a class that truly executes tasks.
TaskQueue: stores the data structure of a Task, which is implemented by a minimum heap. Each heap member is TimeTask. Each Task is ordered by the nextExecutionTime attribute value of TimerTask, the tasks with the smallest nextExecutionTime are at the frontend of the queue, so that they can be executed as early as possible.
3. Locate the cause based on the result
After reading Java. util. Timer's implementation scheme similar to setTimeout (), continue to return to the setTimeout () method in the Javascript section above, and then see why the previous output is not as expected.
var start = new Date;setTimeout(function(){var end = new Date;console.log('Time elapsed:', end - start, 'ms');}, 500);while (new Date - start < 1000) {};
By reading the Code, it is not hard to see that the setTimeout () method is executed before the while () loop, and it declares that "Hopefully" an anonymous function will be executed after Ms. This declaration, that is, the registration of anonymous functions takes effect immediately after the setTimeout () method is executed. The while loop in the last line of the Code will continue to run for 1000 ms. The latency of the output of the anonymous function registered using the setTimeout () method is always greater than 1000 ms, this indicates that the actual call to this anonymous function is blocked by the while () loop. The actual call is executed only after the while () loop is blocked.
In Java. util. in Timer, the solution for scheduled tasks is implemented through multithreading. Task objects are stored in the task queue and executed by a dedicated scheduling thread in the new Child thread. When an asynchronous task is registered using the schedule () method, the scheduling thread starts working immediately in the subthread, and the main thread does not block the running of the task.
This is a major difference between Javascript and Java/C #, that is, Javascript's single-threaded mechanism. In the existing browser environment, the Javascript execution engine is single-threaded. The statements and methods of the main thread block the running of scheduled tasks. The execution engine only needs to finish the statements of the main thread, scheduled tasks are actually executed. The time in this period may be later than the delay time set during Task registration. At this point, Javascript and Java/C # have different mechanisms.
4. Event Loop Model
In a single-threaded Javascript Engine, how does setTimeout () run? Here we will mention the event loop model in the browser kernel. In short, there is a task queue outside the Javascript execution engine. When the setTimeout () method is called in the code, the registered latency method will be handled by other modules in the browser kernel (taking webkit as an example, it is the webcore module). When the latency method reaches the trigger condition, that is, when it reaches the set latency time, this latency method is added to the task queue. This process is handled by other modules in the browser kernel and independent from the main thread of the execution engine. The execution engine completes the method execution in the main thread and reaches the idle state, the task is obtained from the task queue in sequence for execution. This process is a continuous loop process called the event loop model.
Refer to the materials in a speech. The above event loop model can be described.
When the main thread of the Javascript execution engine is running, heap and stack are generated ). The code in the program enters the stack and waits for execution. When the setTimeout () method is called, that is, the WebAPIs method on the right side of the figure, the corresponding module of the browser kernel starts processing the latency method, when the delayed method reaches the trigger condition, the method is added to the task queue for callback. As long as the code in the execution engine stack is completed, the main thread will read the task queue, execute the callback functions that meet the trigger conditions in sequence.
Further explain with examples in the speech
As an example, when the execution engine starts to execute the above Code, it is equivalent to adding a main () method to the execution stack. Start console. log ('Hi'), log ('Hi') method into the stack, console. the log method is a common method supported by the webkit kernel, rather than the WebAPIs method in the preceding figure. Therefore, the log ('Hi') method is immediately output from the stack and executed by the engine.
After the console. log ('Hi') Statement is executed, the log () method runs out of the stack and outputs Hi. Engine continues to add setTimeout (callback, 5000) to the execution stack. The setTimeout () method belongs to the WebAPIs method in the event loop model. When the engine runs the setTimeout () method out of the stack, it submits the function of delayed execution to the corresponding module, that is, the timer module on the right of the image.
When the execution engine releases setTimeout from the stack for execution, the latency processing method is handed over to the webkit timer module for processing, and then the subsequent code is processed immediately, so the log ('sjs') is added to the execution stack, next, log ('sjs') runs out of the stack and outputs SJS. After the execution engine executes console. log ('sjs'), the program is processed, and the main () method is also released.
After the setTimeout method is executed for five seconds, the timer module detects that the delay processing method has reached the trigger condition, and adds the delay processing method to the task queue. At this time, the execution stack of the execution engine is empty, so the engine starts to poll and check whether there are tasks in the task queue that need to be executed, and then checks the delay method that has reached the execution condition, therefore, the latency method is added to the execution stack. The engine finds that the latency method calls the log () method, and then adds the log () method to the stack. Then execute the stack in sequence, output there, and clear the stack.
After the execution stack is cleared, the execution engine continues to poll the task queue and checks whether there are other tasks that can be executed.
5. Implementation of timer in webkit
The execution process of the following code can be thoroughly understood here. The execution engine first writes the setTimeout () method into the stack and submits the delay method to the corresponding kernel module for processing during execution. The engine continues to process the following code. The while statement blocks the engine for 1 second. During this process, the kernel timer module adds the latency Method to the task queue at 0.5 seconds, after the engine executes the stack clearing, the engine puts the latency method into the stack and processes it. The final output time exceeds the expected time.
var start = new Date;setTimeout(function(){var end = new Date;console.log('Time elapsed:', end - start, 'ms');}, 500);while (new Date - start < 1000) {};
The WebAPIs section mentioned in the previous event loop model diagram mentions DOM events, AJAX calls, And setTimeout methods. In the figure, we simply sum them into WebAPIs, they also add the callback function to the task queue to wait for the engine to execute. This is a simplified description. In fact, the browser kernel processes DOM events, AJAX calls, And setTimeout methods. The webkit kernel is out of the javaspt PT execution engine, one important module is the webcore module. html parsing and css style calculation are all implemented by webcore. For the three APIs mentioned in WebAPIs in the figure, webcore provides DOM Binding, network, and timer modules to process the underlying implementation. Here we continue to use setTimeout as an example to see the implementation of the timer module.
The Timer class is an essential basic component of the webkit kernel. by reading the source code, you can fully understand its principles. This article simplifies it and analyzes its execution process.
The latency method registered using the setTimeout () method is passed to the timer module of the webcore component for processing. The key class in timer is TheadTimers, which contains two important members: TimerHeap task queue and SharedTimer method scheduling class. The latency method is encapsulated as a timer object and stored in TimerHeap. Like the Java. util. Timer task queue, TimerHeap uses the data structure of the smallest heap and uses nextFireTime as the keyword. SharedTimer is used as the TimerHeap scheduling class. When the timer object reaches the trigger condition, the latency method is added to the task queue mentioned in the event loop model through the interface related to the browser platform.
TimerHeap adopts the data structure of the smallest heap. tasks with the smallest expected latency are executed first. At the same time, the execution sequence of two tasks with the same expected latency is executed in the order of registration.
var start = new Date;setTimeout(function(){console.log('fn1');}, 20);setTimeout(function(){console.log('fn2');}, 30);setTimeout(function(){console.log('another fn2');}, 30);setTimeout(function(){console.log('fn3');}, 10);console.log('start while');while (new Date - start < 1000) {};console.log('end while');
The above code is output in sequence
start whileend whilefn3fn1fn2another fn2
References
1. Javascript asynchronous programming
2. Detailed description of JavaScript Running Mechanism: Let's talk about Event Loophttp: // www.ruanyifeng.com/blog/2014/10/event-loop.html
3. Philip Roberts: Help, I'm stuck in an event-loop.https: // vimeo.com/96379312
4. How JavaScript Timers Work. http://ejohn.org/blog/how-javascript-timers-work/
5. How WebKit's event model works. http://brrian.tumblr.com/post/13951629341/how-webkits-event-model-works
6. Timer implementation. http://blog.csdn.net/shunzi__1984/article/details/6193023
The preceding section describes the details of the event loop model from setTimeout. For more information, see other related articles in the first PHP community!