[转]只有7行代码的异步调用 js 库

本文介绍了一个仅7行的JavaScript库,用于解决异步函数回调地狱的问题。通过创建一个帮助函数来按顺序执行异步任务,并允许传递参数及设置作用域。

7 lines JavaScript library for calling asynchronous functions

I was surprised by the good feedback for JavaScript template engine in just 20 lines and decided to blog for another small utility function which I'm using often. While we are talking about JavaScript in the browser, most of the operations are asynchronous. We are dealing with callbacks all the time and sometimes we end up with awesome callback hell.

 

Let's say that we have two functions and we want to call them one after each other. They both operate with same variable. The first one sets its value and the second one uses it.

 

    var value;
    var A =function(){
          setTimeout(function(){
               value =10;
          },200);
    };

    var B =function(){
        console.log(value);
    };

    调用方法:
     A();
     B();
     执行结果: undefined
 

So, if we now run A();B(); we will get undefined in the console. It's like that because the A function sets the value asynchronously. What we could do is to send a callback and execute it once the job is done.

 

    var value;
    var A =function(callback){
          setTimeout(function(){
                value =10;
                callback();
          },200);
    };

    var B =function(){
           console.log(value);
    };
     
    A(function(){
         B();
    });
    执行结果: 10

 

This works, but imagine what will happen if we need to run five or more methods. Passing callbacks all the time will lead to messy and unpleasant code.

 

The idea is to write a helper function which accept our workers and handle the whole process. Let's start with the simplest thing:

 

    var queue =function(funcs){
          // magic here
    }
 

So, what we have to do is to run that function by passing the both A and B - queue([A, B]). We need to get the first function and execute it.

 

    var queue =function(funcs){
         var f = funcs.shift();
         f();
    }
 

If you run this code you will see an error TypeError: undefined is not a function. That's because A function doesn't accept callback but it tries to run it. Let's pass one.

 

    var queue =function(funcs){
           varnext=function(){
               // ...
           };
          var f = funcs.shift();
          f(next);
    };
 

The next method is getting called once A finishes its job. That's the perfect place for continuing to the next function in the list. We could arrange the code a bit and we are able to go through the whole array.

 

    var queue =function(funcs){
           varnext=function(){
                var f = funcs.shift();
                f(next);
           };
           next();
    };
 

If we leave the things like that we will reach our goal. I.e. function A is called and just after that B, which prints the correct value of the variable. The key moment here is the usage of shift method. It removes the first element of the array and returns the element. Step by step funcs array becomes empty. So, this could lead to an error. To prove this theory, let's assume that we still need to run the both functions, but we don't know their order. In this case, they both should accept callback and execute it.

    var A =function(callback){
          setTimeout(function(){
               value =10;
               callback();
           },200);
    };

    var B =function(callback){
           console.log(value);
           callback();
    };

 

And of course we got TypeError: undefined is not a function. To prevent this we should check if the funcs array is empty.

    var queue =function(funcs){
           (functionnext(){
                  if(funcs.length >0){
                  var f = funcs.shift();
             f(next);
           }
           })();
    };

 

What I did also is to invoke the next function just after its definition. It saves few bytes.

 

Let's try to cover as many cases as possible. What about the current scope of the executed functions. The this keyword inside the functions probably points the global window object. It will be cool if we set our own scope.

    var queue =function(funcs, scope){
           (functionnext(){
                  if(funcs.length >0){
                      var f = funcs.shift();
                       f.apply(scope,[next]);
                  }
            })();
    };

 

We added one more parameter to the tiny library. Later instead of f(next) we use the apply function, set the scope and pass next as a parameter. Pretty much the same thing.

 

And the last feature which we need is the ability pass arguments between the functions. Of course we don't have an idea how many parameters will be send. That's why we need to use the arguments variable. As you probably know, that variable is available in every JavaScript function and represents the coming parameters. It's something like an array, but not exactly. And because in apply method we need to use real array, a little trick is used.

    var queue =function(funcs, scope){
         (functionnext(){
               if(funcs.length >0){
                      var f = funcs.shift();
                      f.apply(scope,[next].concat(Array.prototype.slice.call(arguments,0)));
                }
           })();
    };

 

And here is the full example which demonstrates all the features of the library.

    var obj ={
           value:null
    };
     
    queue([
           function(callback){
                varself=this;
                setTimeout(function(){
                       self.value =10;
                       callback(20);
                },200);
            },
           function(callback, add){
                 console.log(this.value + add);
                 callback();
           },
           function(){
                  console.log(obj.value);
           }
    ], obj);

 

If you run this code you will see:

    30
    10

 

And to match the lines mentioned in the title of this article we could write the main code in only one line:

  var queue =function(funcs, scope){
      (functionnext(){
          if(funcs.length >0){
             funcs.shift().apply(scope ||{},[next].concat(Array.prototype.slice.call(arguments,0)));
           }
       })();
    };

 

Here is a JSBin to play with:

 

var queue = function(funcs, scope) {
    (function next() {
          if(funcs.length > 0) {
              funcs.shift().apply(scope || {}, [next].concat(Array.prototype.slice.call(arguments, 0)));
          }
    })();
};

var obj = {
    value: null
};


queue([
    function(callback) {
        var self = this;
        setTimeout(function() {
            self.value = 10;
            callback(20);
        }, 200);
    },
    function(callback, add) {
        console.log(this.value + add);
        callback();
    },
    function() {
        console.log(obj.value);
    }
], obj);

queue();

 

原文网址:http://krasimirtsonev.com/blog/article/7-lines-JavaScript-library-for-calling-asynchronous-functions

译文网址:http://blog.jobbole.com/60046/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值