background
The JS execution efficiency needs to be analyzed. Therefore, the execution time of JS needs to be recorded to analyze the JS execution efficiency. However, due to the prevalence of ayNC /await use, when recording the execution time of functions, the length of asynchronous execution will be recorded. As a result, the time record is not accurate and the actual execution time of JS functions cannot be analyzed.
Look at a piece of code
const asyncFn = ms= > new Promise((resolve, reject) = > {
setTimeout(resolve, ms);
});
const record = {
start: 0.time: 0};const fn = async () => {
record.start = performance.now();
await asyncFn(100);
record.time = performance.now() - record.start;
console.log(record.time);
}
fn();
Copy the code
From the browser console, you can see the output of 103.84499999781838. The execution time may vary each time, but it should always be around this value. We can see that the fn function is incorrectly timed due to the presence of aync/await function, including the execution time of the asynchronous function.
When asynchronous functions are executed on the Web side, they generally request external related interfaces, which will not hinder the execution of other JS functions on the Web side and will not cause JS blocking and page jam. Therefore, the execution time of such asynchronous functions is not within our statistical scope, we only count the execution time of code in the Web.
Correct way
We can solve this problem by timing async/await functions separately, as shown in the following code:
const asyncFn = ms= > new Promise((resolve, reject) = > {
setTimeout(resolve, ms);
});
const record = {
start: 0.time: 0};const fn = async () => {
record.start = performance.now();
record.time = performance.now() - record.start;
await asyncFn(100);
record.start = performance.now();
record.time = performance.now() - record.start;
console.log(record.time);
}
fn();
Copy the code
By running it in browse, you can see the result in the console, which is about 0.010000003385357559. You can see that it does solve the impact of asynchronous functions, but you need to do this manually in every async/await area. Don’t you think it’s a hassle?
The target
Use Babel AST’s ability to automatically handle timing problems with async/await by analyzing the code’s AST, avoiding manual input of large amounts of duplicate code and reducing the chance of error.
tool
A good tool is a ladder to a goal, and Babel is the ladder.
Visual AST Platform
Click the jump, you can easily view the code AST structure, help to understand the relationship between code and AST structure, twice the result with half the effort.
By copying the above two pieces of code into the AST, we can see the AST structure of the above code:
For us, the important thing to focus on is the section with type Program, which describes the AST structure of the code snippet.
Babel Describes the process for generating an AST
Before you write a Babel plugin, you need to have a clear understanding of the Babel cycle, so that you can know when your plugin is working and what effect it is achieving.
Introduction to Babel on Github Click to jump to view the basic introduction to Babel plug-in content, including the definition type of each node in the AST, traversal mode, plug-in entry, etc. You are advised to check it in detail. Since the documentation is detailed enough to facilitate a query manual for the various API uses during plug-in writing, I won’t cover it in this article.
Find the processing target node
Since we are only dealing with async/await functions this time, we will focus on AST nodes of type AwaitExpression
Babel plug-in writing format
In the format of the Babel plug-in, we need to export by default a function that takes a Babel object as an argument and returns an object with a Visitor property:
export default function (babel) {
return {
visitor: {
AwaitExpression(path){},}}; }Copy the code
In the Visitor object, we define the type of node that we need to process, and we can refer to the Github code for the specific logic
Babel plug-in processing flow
The specific process can refer to the code and unit test section on Github, which is mainly divided into three parts:
- Parse the code string to generate the AST, primarily using Babylon
- Traverse the AST for node processing, mainly using babel-traverse. Here, to generate the AST that needs to be inserted into the code, the tool babel-Template is used, which is super easy to use, and you don’t have to manually build the REQUIRED AST yourself with babel-types
- Code is generated based on the processed AST, mainly using Babel-Generator
conclusion
This simple Babel plug-in is complete, of course, if the real environment to use, there are many other boundary conditions to consider, here from the overall introduction of Babel plug-in writing mode, easy to follow similar needs, can quickly start.
Plug-in code Example
The code examples in this article can be found on Github for effect testing and new plug-in development.