background

I wrote this article because a colleague used global variables incorrectly and caused a bug. So before explaining the title, a little background on the business.

Very simple, is that there is a page can handle a certain business, the business is divided into two types, can be switched at will. The problem is that the page is initialized with type A by default, so the front end will call the back end with type A arguments to about 3 interfaces, let’s call them interface 1, interface 2 and interface 3. Three interfaces of the request parameters depend on the response parameters of interfaces 1 and 2, return the data interfaces 1 and 2 will show to the front, and then call interface 3 when will return from interfaces 1 and 2 of the parameters in the data is passed to the interface of 3, then will return to the interface of 3 data show, to initialize the page loaded.

As you can see from the following page sketch, interfaces 1,2, and 3 depend on the type to complete the corresponding logical processing. In the interface call, interface 1,2 (in no order) must be called first, then interface 3. After that, when switching from type A to type B, interfaces 1,2, and 3 will be reloaded according to type B to display the corresponding data of type B.

Troubleshoot problems

The business rules are pretty simple. However, it was found in the test that when the page was initialized, it was quickly switched to type B, and an error window “System error, lack of necessary parameters” popped up at the front end. The problem occurred occasionally but could be repeated stably.

Through the analysis found that is the question of the front-end interface call order, specific call interface is 3, don’t get the need data (interface logic is roughly through the front end of the 3 spread parameters 1 and 2 interfaces 1 and 2 on the cached data, the Key and type of cache) from the point of view of representation is in the call interface 3, Interface 1 or interface 2 has not been called, so interface 3 cannot get the data it needs from the cache.

With this question in mind, I looked at the front-end code to see if there was really a problem with the order in which the interfaces were called, and found that there was no problem with the order in which the interfaces were called. So what’s the problem?

Through checking the front-end code, I found A problem. The front-end set A global variable to record the current business type (such as type A and type B), which was transferred when interface 1,2 and 3 were called to transfer the business type. Seeing this, you may be able to understand why it is prudent to use global variables. The problem is caused by improper use of global variables.

The analysis reason

Let’s take a look at how it happened.

When does the global variable in the front end, which records the current business type, change its value?

Yes, it is when switching business types that the current business type A or B is recorded. When the initialization default is type A, the interface calls type A: interface 1 (A) -> interface 2 (A) -> Interface 3 (A). When switching to type B, A series of interface calls are triggered, the same as for type A. Type B: Interface 1 (B) -> Interface 2 (B) -> interface 3 (B) is called.

The key is that when switching to type B, there may be such A problem. Interface 1,2 is called normally, that is, the business type passed is A, but just before calling interface 3, the global variable that records the current business type is changed to B. So at this time was initialized with the interface of 3 to the type of business by the expected A into B, before the interface 1, 2 are passed by A type parameter, so the background data storage is A type of, but at the moment because of the change of the global variables, interface 3 pass the type of business was A to B, so in the business logic of interface 3, If the data cannot be cached according to service type B, an error message “System error, necessary parameters are missing” is displayed when the parameters are verified on the back-end.

Look at this, do you think this is a bit like Java multi-threaded variable sharing? Multithreaded variable sharing can also cause problems when one thread is using a variable and it is suddenly changed by another thread, causing that thread to get dirty data. Only one thread can modify the shared variable at the same time. That is, the relationship between multiple threads is mutually exclusive to the resource. In Java, locks are used to ensure the security of operations.

So in this case, what’s the analogy? We can compare the series of interfaces that go through when type A is selected to the A thread; Compare the series of interfaces that type B goes through to thread B. The two threads execute the same process and method, but the specific values of parameters are different. Thread A and thread B respectively execute three steps and each step will take shared variables as parameters and pass them to the background. The operation of switching type to change current business type (biz_type) is denoted as C thread. Basically, threads A and B read biz_type and thread C modifies biz_type. This can be interpreted as three threads sharing a variable, and switching business types on the page can be seen as a thread rotation, so it can’t be controlled without errors.

Problem solving

How to solve the problem once you understand the cause of the problem? The root cause of the problem is that thread C changed the value of biz_type before thread A finished. As A result, thread A’s three steps got different values of biz_type. As a result, the background cannot get cached data according to the type, and an error is reported. So, the key to solving this problem is to start with this global variable and look at the front-end code: When we switch types, we pass parameters based on the currently selected type. When we are selected, we know which type is selected, so we can clearly call the interface to pass the corresponding type field, rather than assign the global variable and fetch the global variable from the interface.

Modify before:

var biz_type = 'A'; //change radio function changeRadio(){if(#('#bizType_A').is(':checked')){biz_type = 'A'; // Change the variable value api_1(); }else{ biz_type = 'B'; // Change the variable value api_1(); } } //function1 function api_1(){ //get biz_type //send ajax with biz_typ if(data.success){ api_2(); }else{ alert(data.msg); } } //function2 function api_2(){ //get biz_type //send ajax with biz_typ if(data.success){ api_3(); }else{ alert(data.msg); } } //function3 function api_3(){ //get biz_type //send ajax with biz_type if(data.success){ jump_to_success(); }else{ alert(data.msg); }}Copy the code

Revised:

//change radio function changeRadio(){ if(#('#bizType_A').is(':checked')){ api_1('A'); }else{api_1('B'); //function1 function api_1(biz_type){//send ajax with biz_typ if(data.success){data.api_2 (biz_type); }else{ alert(data.msg); } } //function2 function api_2(biz_type){ //send ajax with biz_typ if(data.success){ api_3(biz_type); }else{ alert(data.msg); } } //function3 function api_3(biz_type){ //send ajax with biz_type if(data.success){ jump_to_success(); }else{ alert(data.msg); }}Copy the code

After modification, use the parameter passing method, so that the same biz_type value can be obtained when the process comes down. In addition, the value of biz_type can not be modified or thread B can not be executed before thread A finishes, that is, the type cannot be changed before the process of type A finishes. The flag bit can be used to determine whether process A has finished, and thus whether it can be switched to type B.

conclusion

However, this problem is not big, the backend has done the parameter verification, but in order to improve the user experience, this problem must be solved. This was the result of a small oversight on the part of the front-end developer, who would never have foreseen such a problem when he wrote the code. He would never have expected global variables to cause such a problem, and he would never have expected the user to switch types before the page was initialized. But these for a fledgling front-end development, understandable, right when the accumulation of experience. Remember to pass parameters as far as possible do not use global variables, if you must use, it is a point to grasp not to appear similar problems.

Problems are not terrible, in the problem of growth, accumulation of experience, is the most important.

[end] Closure map from the network, infringement contact deletion.