JS函數生成器,function* () {}


入門

  簡單來說,用法如下:

復制代碼
    function* fn() {
        console.log(1);
        //暫停!
        yield;
        //調用next方法繼續執行
        console.log(2);
    }
    var iter = fn();
    iter.next(); //1
    iter.next(); //2
復制代碼

  1、函數生成器特點是函數名前面有一個‘*’

  2、通過調用函數生成一個控制器

  3、調用next()方法開始執行函數

  4、遇到yield函數將暫停

  5、再次調用next()繼續執行函數

 

消息傳遞

  除了暫停和繼續執行外,生成器同時支持傳值。

  用法如下:

復制代碼
    function* fn() {
        var a = yield 'hello';
        yield;
        console.log(a);
    }
    var iter = fn();
    var res = iter.next();
    console.log(res.value); //hello
    iter.next(2);
    iter.next(); //2
復制代碼

  可以看到,yield后面有一個字符串,在第一次調用next時,暫停在這里且返回給了iter.next()。

  而暫停的地方是一個賦值語句,需要一個變量給a,於是next()方法中傳了一個參數2替換了yield,最后打印a得到了2。

 

異步應用   

  通過yield來實現異步控制流程:

復制代碼
    function fn(a, b) {
        //假設這是一個ajax請求
        ajax('url' + a + b, function(data) {
            //數據請求到會執行it.next
            it.next(data);
        });
    }
    //這里是函數生成器
    function* g() {
        //當異步操作完畢yield會得到值
        //這里會自動繼續執行
        var text = yield fn(a, b);
        console.log(text);
    }
    var it = g();
    it.next();
復制代碼

  這里做了簡化處理,忽略了一些錯誤處理。

  確實很巧妙,通過回調函數來繼續執行函數生成器,然后得到數據。

  然而,直接在回調里拿數據不行么。書上講,這樣異步操作符合大腦思考模式,函數的執行看起來‘同步’了。

 

yield+promise

  重點來了。

  先回憶之前promise對異步的實現: 

復制代碼
    function request(url) {
        return new Promise(function(resolve, reject) {
            //ajax異步請求完成會調用resolve決議
            ajax(url, resolve);
        });
    }
    request('url').then(function(res) {
        console.log(res);
    })
復制代碼

  流程大概是調用函數傳入url,由於會立即決議,觸發ajax請求函數。異步請求完調用調用回調函數,也就是resolve,然后根據返回的resolve調用then方法獲取數據。

  現在將yield與promise綜合在一起:

復制代碼
    function foo(x) {
        return request('url' + x);
    }
    //等待promise決議值返回
    function* fn() {
        var text = yield foo(1);
    }
    var it = fn();
    //返回一個promise
    var p = it.next().value;
    //對promise處理
    p.then(function(text) {
        //這里繼續執行生成器
        it.next(text);
    })
復制代碼

 

封裝

  可以將上面的yield+promise進行封裝,得到下面的函數:

復制代碼
    function run(gen) {
        //獲取除了生成器本身以外的參數
        var args = [].slice.call(arguments, 1),
            it;
        //it = main()
        it = gen.apply(this, args);
        return Promise.resolve().then(function handleNext(value) {
            //第一次啟動無value
            var next = it.next(value);
            return (function handleResult(next) {
                //執行完畢返回
                if (next.done) {
                    return next.value;
                } else {
                    //如果還有就決議next.value傳給handleNext
                    return Promise.resolve(next.value).then(handleNext, function(err) {});
                }
            })(next);
        });
    }
    //這是一個函數生成器
    function* main() {
        //...
    };
    //該調用會自動異步運行直到結束
    run(main);
復制代碼

  如果有兩個異步操作,獲取到返回的兩個數據后,再進行第三個異步操作,可以這么做:

復制代碼
    function foo() {
        var p1 = request('url1'),
            p2 = request('url2');
        //每一個request異步請求完成后會自動解除yield
        var r1 = yield p1,
            r2 = yield p2;
        var r3 = yield request('url3' + r1 + r2);
        console.log(r3);
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM