Home > Web Front-end > JS Tutorial > body text

Detailed explanation of JavaScript chain structure serialization

黄舟
Release: 2017-03-04 15:21:22
Original
1131 people have browsed it

1. Overview

In JavaScript, there are too many chain pattern codes, as follows:

if_else:

if(...){
    //TODO
}else if(...){
    //TODO
}else{
    //TODO
}
Copy after login

switch:

switch(name){
    case ...:{
        //TODO
        break;
    }
    case ...:{
        //TODO
        break;
    }
    default:{
        //TODO    
    }
}
Copy after login

Question: Such as the above chain codes, what if we want to flatten them and chain them? As follows:

//fn1,f2,f3为处理函数
_if(fn1)._elseIf(fn2)._else(fn3);
Copy after login
Copy after login

Let’s try to implement it together.

2. Flatten the chain code

Suppose now we have the following chain code:

if(name === 'Monkey'){
    console.log('yes, I am Monkey');
}else if(name === 'Dorie'){
    console.log('yes, I am Dorie');
}else{
    console.log('sorry, over for ending!');
}
Copy after login

Okay, now we will "flatten it step by step" change".

In fact, looking at the above code, it is not difficult to find that the if...else format is actually a singly linked list in the data structure. Then, initially use JavaScript to implement a singly linked list, as follows:

var thens = [];
thens.resolve = function(name){
    for(var i = 0, len = this.length; i < len;i++){
        if(this[i](name) !== &#39;next&#39;){
            break;
        }
    }
}
thens.push(f1, f2, f3);
Copy after login

Where f1, f2, f3 are judgment functions, and we assume that if f1, f2, f3 returns 'next', continue to search downwards, otherwise, stop searching downwards. As follows:

function f1(name){
    if(name === &#39;Monkey&#39;){
        console.log(&#39;yes, I am Monkey&#39;);
    }else{
        return &#39;next&#39;;
    }
}
function f2(name){
    if(name === &#39;Dorie&#39;){
        console.log(&#39;yes, I am Dorie&#39;);
    }else{
        return &#39;next&#39;;
    }
}
function f3(){
    console.log(&#39;sorry, over for ending!&#39;);
}
Copy after login

Okay, this is the pattern of the linked list.

But, our ultimate goal is to achieve the following?

//fn1,f2,f3为处理函数
_if(fn1)._elseIf(fn2)._else(fn3);
Copy after login
Copy after login

You might say, wouldn’t it be nice to change the above code to the following? ! !

thens.push(f1).push(f2).push(f3).resolve();
Copy after login

But, JavaScript’s push method returns the new length of the array, not the array object.

So, Then we can only write a new add method, which has the same effect as push, but returns an array object. As follows:

thens.add = function(f){
    if(typeof f === &#39;function&#39;){
        this.push(f);
        return this;        
    }        
}
Copy after login

The test code is as follows:

var thens = [];
thens.add = function(f){
    if(typeof f === &#39;function&#39;){
        this.push(f);
        return this;        
    }        
}
thens.resolve = function(name){
    for(var i = 0, len = this.length; i < len;i++){
        if(this[i](name) !== 'next'){
            break;
        }
    }    
}
thens.add(f1).add(f2).add(f3).resolve();
Copy after login

However, this has a disadvantage. We bind the add and resolve methods to the global variable thens. You can't just copy and paste the method every time you create an array, so the refactored code is as follows:

function Slink(){
    this.thens = [];
    this.thens.add = function(f){
        if(typeof f === &#39;function&#39;){
            this.push(f);
            return this;        
        }        
    }
    this.thens.resolve = function(name){
        for(var i = 0, len = this.length; i < len;i++){
            if(this[i](name) !== &#39;next&#39;){
                break;
            }
        }    
    }
}
Copy after login

Obviously, public methods like add and resolve are not created every time they are instantiated. Scientifically, so, use prototype to continue deforming on the original basis, as follows:

function Slink(){
    this.thens = [];
}
Slink.prototype = {
    add: function(f){
            if(typeof f === &#39;function&#39;){
                this.thens.push(f);
                return this;        
            }        
    },
    resolve: function(name){
            for(var i = 0, len = this.thens.length; i < len; i++){
                if(this.thens[i](name) !== &#39;next&#39;){
                    break;
                }
            }    
    }
}
Copy after login

The test code is as follows:

var thens = new Slink();
thens.add(f1).add(f2).add(f3);
thens.resolve();
Copy after login

Not bad, but like this, we fail every time It is a bit troublesome to manually create a new Slink, so we encapsulate the new Slink process into a function, just like jQuery, as follows:

function $go(f){
    return new Slink(f);
}
function Slink(f){
    this.thens = [];
    this.thens.push(f);
}
Slink.prototype = {
    add: function(f){
            if(typeof f === &#39;function&#39;){
                this.thens.push(f);
                return this;        
            }        
    },
    resolve: function(name){
            for(var i = 0, len = this.thens.length; i < len; i++){
                if(this.thens[i](name) !== &#39;next&#39;){
                    break;
                }
            }    
    }
}
Copy after login

The test code is as follows:

$go(f1).add(f2).add(f3).resolve();
Copy after login

Okay, you’re done, now comes the syntax sugar problem. The code is as follows:

function _if(f){
    return new Slink(f);
}
function Slink(f){
    this.thens = [];
    this.thens.push(f);
}
Slink.prototype = {
    _elseIf: function(f){
            if(typeof f === &#39;function&#39;){
                this.thens.push(f);
                return this;        
            }        
    },
    _else: function(f){
            return this._elseIf(f);
    },
    resolve: function(name){
            for(var i = 0, len = this.thens.length; i < len; i++){
                if(this.thens[i](name) !== &#39;next&#39;){
                    break;
                }
            }
            return this;            
    }
}
Copy after login

The test code is as follows:

_if(f1)._elseIf(f2)._else(f3).resolve();
Copy after login
Copy after login

Of course, except for using arrays In this way, you can also use closure to achieve a chain flattening effect, as follows:

var func = Function.prototype;
func._else = func._elseIf = function(fn){
    var _this = this;
    return function(){
        var res = _this.apply(this,arguments);
        if(res==="next"){  //值为Boolean
            return fn.apply(this,arguments);
        }
        return res;
    }
}
Copy after login

The test code is as follows:

function f1(name){
    if(name === &#39;Monkey&#39;){
        console.log(&#39;yes, I am Monkey&#39;);
    }else{
        return &#39;next&#39;;
    }
}
function f2(name){
    if(name === &#39;Dorie&#39;){
        console.log(&#39;yes, I am Dorie&#39;);
    }else{
        return &#39;next&#39;;
    }
}
function f3(){
    console.log(&#39;sorry, over for ending!&#39;);
}
f1._elseIf(f2)._else(f3)('Dorie');
Copy after login

三, Asynchronous code chain flattening

What we discussed above are all synchronous processes. What if there is an asynchronous situation in the chained calling function?

What's the meaning? As follows:

function f1(name){
    setTimeout(function(){
        if(name === &#39;Monkey&#39;){
            console.log(&#39;yes, I am Monkey&#39;);
        }else{
            return &#39;next&#39;;
        }
    }, 2000);
}
function f2(name){
    if(name === &#39;Dorie&#39;){
        console.log(&#39;yes, I am Dorie&#39;);
    }else{
        return &#39;next&#39;;
    }
}
function f3(){
    console.log(&#39;sorry, over for ending!&#39;);
}
Copy after login

We use setTimeout to make f1 asynchronous. According to the logic of the above code, it should be judged whether to execute f2 after f1 is completely executed (including setTimeout execution), but is this really the case?

The test code is as follows:

_if(f1)._elseIf(f2)._else(f3).resolve();
Copy after login
Copy after login

The result of executing the code is that nothing is output.

Why?

Because JavaScript is single-threaded. See (here) for details

So how to solve it?

Since there is asynchronous code and the subsequent chain must be processed after the asynchronous code, then we wait for the asynchronous code to be executed before executing the subsequent chain, as follows:

function f1(name){
    setTimeout(function(){
        if(name === &#39;Monkey&#39;){
            console.log(&#39;yes, I am Monkey&#39;);
        }else{
            //处理后续链
            this.resolve(name, 1);//1代表下一个需处理函数在数组中的位置
        }
    }.bind(this), 2000);
}
Copy after login

Okay , since in the function, we used this, which represents the Slink object, and changed the resolve method, it is necessary to fine-tune the Slink constructor and prototype chain, as follows:

function Slink(f){
    this.thens = [];
    this.thens.push(f.bind(this));
}
Slink.prototype = {
    _elseIf: function(f){
            if(typeof f === &#39;function&#39;){
                this.thens.push(f.bind(this));
                return this;        
            }        
    },
    _else: function(f){
            return this._elseIf(f.bind(this));
    },
    resolve: function(name, flag){
            for(var i = flag, len = this.thens.length; i < len; i++){
                if(this.thens[i](name) !== &#39;next&#39;){
                    break;
                }
            }
            return this;            
    }
}
Copy after login

The test code is as follows:

function f1(name){
    setTimeout(function(){
        if(name === &#39;Monkey&#39;){
            console.log(&#39;yes, I am Monkey&#39;);
        }else{
            //处理后续链
            this.resolve(name, 1);//1代表下一个需处理函数在数组中的位置
        }
    }.bind(this), 2000);
}
function f2(name){
    if(name === 'Dorie'){
        console.log('yes, I am Dorie');
    }else{
        return 'next';
    }
}
function f3(){
    console.log('sorry, over for ending!');
}
_if(f1)._elseIf(f2)._else(f3).resolve('',0);
Copy after login

Haha, if you know Promise, do you think it is so similar?

Yes, the purpose is the same, to achieve the purpose of flattening asynchronous code, but the code here is much simpler than Promise. For details about Promise, see (here).

The above is the detailed explanation of JavaScript chain structure serialization. For more related content, please pay attention to the PHP Chinese website (www.php.cn)!


##

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!