美文网首页
异步javascript,callback、Promise?我们

异步javascript,callback、Promise?我们

作者: uncle_charlie | 来源:发表于2016-08-16 22:19 被阅读225次

    ES6提供了两个处理异步Js的特性:Promise和Generator。我们这里会介绍这两个新特性,并在最后讲解如何使用Generator。为了使本文更加贴近实际,我们使用一个网络请求库request来作为异步任务的实例使用。

    request库是一个强大而简单易用的网络请求库。她支持http和https,也支持redirect。以后在新的工作里也许就会用得着。

    基本概念

    如果你使用过其他的编程语言,那么一般调用方法的时候是这样的:

    let result = function(...);
    console.log(`${result}`);
    

    他们都是顺序执行,方法执行完成之后打印结果。

    但是在nodejs里,对于异步执行的js是行不通的,比如异步下载一个文件、HTTP访问一个接口等。除非获取result的方法调用可以等待,一直到结果返回之后再调用console.log(${result});

    在ES6之前,代码是没办法停下来的。所以我们只有使用回调:

    var request = require('request');
    
    request('http://www.baidu.com', (err, res, body) => {
        if (err) {
            console.log(`ERROR:- ${err}`);
            return;
        }
    
        console.log(`Request done`);
    });
    

    request执行完成的时候就会调用(err, res, body) => {}的回调。

    Promise是一个更好的处理回调的方法。更多内容可以看这里

    // 1
    var Promise = require('bluebird');
    var request = require('request');
    
    // 2
    Promise.promisifyAll(request);
    
    // 3
    request.getAsync('http://www.baidu.com').then((data)=>{
        console.log('request');
        console.log(`${data.body}`);
    });
    
    1. 引入bluebird库。bluebird就是一个在ES5下实现Promise的库。
    2. 使用bluebird库把request库全部的方法都转化为返回Promise对象的方法。在调用这些方法的时候只需要在原方法名称后加Async的后缀。
    3. 调用Promise化的方法。这时你可以调用then方法了。

    ES6中可以使用Generator了。不严谨的说Generator主要由两部分组成。一个是function关键字后面的*****号,一个是方法体中的yield语句。如:

    function* foo(x) {
      var y = 2 * (yield (x + 1));
      var z = yield (y / 3);
      return (x + y + z);
    }
    
    var b = foo(5);
    b.next() // { value:6, done:false }
    b.next(12) // { value:8, done:false }
    b.next(13) // { value:42, done:true }
    

    最后结果是多少?

    使用第三方库可以使得Generator方法暂停、在运行。基本上是这样的:

    var Promise = require('bluebird');
    var request = require('request');
    
    Promise.promisifyAll(request);
    
    var testFunc = Promise.coroutine(function *() {
        console.log('hello world');
        // let result = yield Promise.resolve('hello yield world');
        let result = yield request.getAsync('http://www.baidu.com');
        console.log(`${result}`);
        return result;
    });
    
    testFunc().then(value => {
        console.log(`### ${value}`);
    });
    

    注意:yield后面的语句必须是一个Promise,最后return的也是一个Promise。

    request.getAsync('http://www.baidu.com');
        console.log(`${result}`);
        return result;
    });
    

    在上文已经介绍过,这个是被bluebird库Promise化之后的方法调用,返回的就是一个Promise。

    代码

    这里深入探讨一下Generator。那么,就请主角入场:

    function *echo(text, delay = 0) {
        const caller = yield;
        setTimeout(() => caller.success(text), delay);
    };
    

    这是一个Generator方法,在执行的时候会在const caller = yield;这一句暂停,等待获取caller对象。在下面的例子里echo方法会被顺序的执行三次:

    run(function* () {
        console.log(yield echo('this'));
        console.log(yield echo('is'));
        console.log(yield echo('a test'));
    });
    
    //Output:
    //this
    //is
    //a test
    

    上面的代码的并行执行例子:

    run(function* parallelEchos() {
        let startTime = Date.now();
        let texts = yield [
            echo('this', 1000),
            echo('is', 900),
            echo('a test', 800)
        ];
        console.log(`##Parallex:- ${texts}`);
        console.log(`##Time used:- ${Date.now() - startTime}`);
    });
    
    //Output:
    //##Parallex:- this,is,a test
    //##Time used:- 1005
    

    如果在一个Generator方法里yield一个Generator方法数组的话,数组里的这些方法会并行执行。最后耗时远小于顺序执行的时间。

    最后

    ES7的proposal里有一个async-await关键字的组合。解决了一个Generator非常的大的问题。Generator需要调用next方法来不断的往下执行yield语句,要自动执行就需要一个执行器,比如TJ的cobluebirdcoroutine方法实现了这个功能。

    相关文章

      网友评论

          本文标题:异步javascript,callback、Promise?我们

          本文链接:https://www.haomeiwen.com/subject/adapsttx.html