国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

ES6 系列之 Generator 的自動執(zhí)行

sunsmell / 3009人閱讀

摘要:將異步操作進(jìn)行包裝,暴露出回調(diào)函數(shù),在回調(diào)函數(shù)里面交回執(zhí)行權(quán)。其次,我們會使用將回調(diào)函數(shù)包裝成一個,然后統(tǒng)一的添加函數(shù)。

單個異步任務(wù)
var fetch = require("node-fetch");

function* gen(){
    var url = "https://api.github.com/users/github";
    var result = yield fetch(url);
    console.log(result.bio);
}

為了獲得最終的執(zhí)行結(jié)果,你需要這樣做:

var g = gen();
var result = g.next();

result.value.then(function(data){
    return data.json();
}).then(function(data){
    g.next(data);
});

首先執(zhí)行 Generator 函數(shù),獲取遍歷器對象。

然后使用 next 方法,執(zhí)行異步任務(wù)的第一階段,即 fetch(url)。

注意,由于 fetch(url) 會返回一個 Promise 對象,所以 result 的值為:

{ value: Promise {  }, done: false }

最后我們?yōu)檫@個 Promise 對象添加一個 then 方法,先將其返回的數(shù)據(jù)格式化(data.json()),再調(diào)用 g.next,將獲得的數(shù)據(jù)傳進(jìn)去,由此可以執(zhí)行異步任務(wù)的第二階段,代碼執(zhí)行完畢。

多個異步任務(wù)

上節(jié)我們只調(diào)用了一個接口,那如果我們調(diào)用了多個接口,使用了多個 yield,我們豈不是要在 then 函數(shù)中不斷的嵌套下去……

所以我們來看看執(zhí)行多個異步任務(wù)的情況:

var fetch = require("node-fetch");

function* gen() {
    var r1 = yield fetch("https://api.github.com/users/github");
    var r2 = yield fetch("https://api.github.com/users/github/followers");
    var r3 = yield fetch("https://api.github.com/users/github/repos");

    console.log([r1.bio, r2[0].login, r3[0].full_name].join("
"));
}

為了獲得最終的執(zhí)行結(jié)果,你可能要寫成:

var g = gen();
var result1 = g.next();

result1.value.then(function(data){
    return data.json();
})
.then(function(data){
    return g.next(data).value;
})
.then(function(data){
    return data.json();
})
.then(function(data){
    return g.next(data).value
})
.then(function(data){
    return data.json();
})
.then(function(data){
    g.next(data)
});

但我知道你肯定不想寫成這樣……

其實,利用遞歸,我們可以這樣寫:

function run(gen) {
    var g = gen();

    function next(data) {
        var result = g.next(data);

        if (result.done) return;

        result.value.then(function(data) {
            return data.json();
        }).then(function(data) {
            next(data);
        });

    }

    next();
}

run(gen);

其中的關(guān)鍵就是 yield 的時候返回一個 Promise 對象,給這個 Promise 對象添加 then 方法,當(dāng)異步操作成功時執(zhí)行 then 中的 onFullfilled 函數(shù),onFullfilled 函數(shù)中又去執(zhí)行 g.next,從而讓 Generator 繼續(xù)執(zhí)行,然后再返回一個 Promise,再在成功時執(zhí)行 g.next,然后再返回……

啟動器函數(shù)

在 run 這個啟動器函數(shù)中,我們在 then 函數(shù)中將數(shù)據(jù)格式化 data.json(),但在更廣泛的情況下,比如 yield 直接跟一個 Promise,而非一個 fetch 函數(shù)返回的 Promise,因為沒有 json 方法,代碼就會報錯。所以為了更具備通用性,連同這個例子和啟動器,我們修改為:

var fetch = require("node-fetch");

function* gen() {
    var r1 = yield fetch("https://api.github.com/users/github");
    var json1 = yield r1.json();
    var r2 = yield fetch("https://api.github.com/users/github/followers");
    var json2 = yield r2.json();
    var r3 = yield fetch("https://api.github.com/users/github/repos");
    var json3 = yield r3.json();

    console.log([json1.bio, json2[0].login, json3[0].full_name].join("
"));
}

function run(gen) {
    var g = gen();

    function next(data) {
        var result = g.next(data);

        if (result.done) return;

        result.value.then(function(data) {
            next(data);
        });

    }

    next();
}

run(gen);

只要 yield 后跟著一個 Promise 對象,我們就可以利用這個 run 函數(shù)將 Generator 函數(shù)自動執(zhí)行。

回調(diào)函數(shù)

yield 后一定要跟著一個 Promise 對象才能保證 Generator 的自動執(zhí)行嗎?如果只是一個回調(diào)函數(shù)呢?我們來看個例子:

首先我們來模擬一個普通的異步請求:

function fetchData(url, cb) {
    setTimeout(function(){
        cb({status: 200, data: url})
    }, 1000)
}

我們將這種函數(shù)改造成:

function fetchData(url) {
    return function(cb){
        setTimeout(function(){
            cb({status: 200, data: url})
        }, 1000)
    }
}

對于這樣的 Generator 函數(shù):

function* gen() {
    var r1 = yield fetchData("https://api.github.com/users/github");
    var r2 = yield fetchData("https://api.github.com/users/github/followers");

    console.log([r1.data, r2.data].join("
"));
}

如果要獲得最終的結(jié)果:

var g = gen();

var r1 = g.next();

r1.value(function(data) {
    var r2 = g.next(data);
    r2.value(function(data) {
        g.next(data);
    });
});

如果寫成這樣的話,我們會面臨跟第一節(jié)同樣的問題,那就是當(dāng)使用多個 yield 時,代碼會循環(huán)嵌套起來……

同樣利用遞歸,所以我們可以將其改造為:

function run(gen) {
    var g = gen();

    function next(data) {
        var result = g.next(data);

        if (result.done) return;

        result.value(next);
    }

    next();
}

run(gen);
run

由此可以看到 Generator 函數(shù)的自動執(zhí)行需要一種機(jī)制,即當(dāng)異步操作有了結(jié)果,能夠自動交回執(zhí)行權(quán)。

而兩種方法可以做到這一點。

(1)回調(diào)函數(shù)。將異步操作進(jìn)行包裝,暴露出回調(diào)函數(shù),在回調(diào)函數(shù)里面交回執(zhí)行權(quán)。

(2)Promise 對象。將異步操作包裝成 Promise 對象,用 then 方法交回執(zhí)行權(quán)。

在兩種方法中,我們各寫了一個 run 啟動器函數(shù),那我們能不能將這兩種方式結(jié)合在一些,寫一個通用的 run 函數(shù)呢?我們嘗試一下:

// 第一版
function run(gen) {
    var gen = gen();

    function next(data) {
        var result = gen.next(data);
        if (result.done) return;

        if (isPromise(result.value)) {
            result.value.then(function(data) {
                next(data);
            });
        } else {
            result.value(next)
        }
    }

    next()
}

function isPromise(obj) {
    return "function" == typeof obj.then;
}

module.exports = run;

其實實現(xiàn)的很簡單,判斷 result.value 是否是 Promise,是就添加 then 函數(shù),不是就直接執(zhí)行。

return Promise

我們已經(jīng)寫了一個不錯的啟動器函數(shù),支持 yield 后跟回調(diào)函數(shù)或者 Promise 對象。

現(xiàn)在有一個問題需要思考,就是我們?nèi)绾潍@得 Generator 函數(shù)的返回值呢?又如果 Generator 函數(shù)中出現(xiàn)了錯誤,就比如 fetch 了一個不存在的接口,這個錯誤該如何捕獲呢?

這很容易讓人想到 Promise,如果這個啟動器函數(shù)返回一個 Promise,我們就可以給這個 Promise 對象添加 then 函數(shù),當(dāng)所有的異步操作執(zhí)行成功后,我們執(zhí)行 onFullfilled 函數(shù),如果有任何失敗,就執(zhí)行 onRejected 函數(shù)。

我們寫一版:

// 第二版
function run(gen) {
    var gen = gen();

    return new Promise(function(resolve, reject) {

        function next(data) {
            try {
                var result = gen.next(data);
            } catch (e) {
                return reject(e);
            }

            if (result.done) {
                return resolve(result.value)
            };

            var value = toPromise(result.value);

            value.then(function(data) {
                next(data);
            }, function(e) {
                reject(e)
            });
        }

        next()
    })

}

function isPromise(obj) {
    return "function" == typeof obj.then;
}

function toPromise(obj) {
    if (isPromise(obj)) return obj;
    if ("function" == typeof obj) return thunkToPromise(obj);
    return obj;
}

function thunkToPromise(fn) {
    return new Promise(function(resolve, reject) {
        fn(function(err, res) {
            if (err) return reject(err);
            resolve(res);
        });
    });
}

module.exports = run;

與第一版有很大的不同:

首先,我們返回了一個 Promise,當(dāng) result.done 為 true 的時候,我們將該值 resolve(result.value),如果執(zhí)行的過程中出現(xiàn)錯誤,被 catch 住,我們會將原因 reject(e)

其次,我們會使用 thunkToPromise 將回調(diào)函數(shù)包裝成一個 Promise,然后統(tǒng)一的添加 then 函數(shù)。在這里值得注意的是,在 thunkToPromise 函數(shù)中,我們遵循了 error first 的原則,這意味著當(dāng)我們處理回調(diào)函數(shù)的情況時:

// 模擬數(shù)據(jù)請求
function fetchData(url) {
    return function(cb) {
        setTimeout(function() {
            cb(null, { status: 200, data: url })
        }, 1000)
    }
}

在成功時,第一個參數(shù)應(yīng)該返回 null,表示沒有錯誤原因。

優(yōu)化

我們在第二版的基礎(chǔ)上將代碼寫的更加簡潔優(yōu)雅一點,最終的代碼如下:

// 第三版
function run(gen) {

    return new Promise(function(resolve, reject) {
        if (typeof gen == "function") gen = gen();

        // 如果 gen 不是一個迭代器
        if (!gen || typeof gen.next !== "function") return resolve(gen)

        onFulfilled();

        function onFulfilled(res) {
            var ret;
            try {
                ret = gen.next(res);
            } catch (e) {
                return reject(e);
            }
            next(ret);
        }

        function onRejected(err) {
            var ret;
            try {
                ret = gen.throw(err);
            } catch (e) {
                return reject(e);
            }
            next(ret);
        }

        function next(ret) {
            if (ret.done) return resolve(ret.value);
            var value = toPromise(ret.value);
            if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
            return onRejected(new TypeError("You may only yield a function, promise " +
                "but the following object was passed: "" + String(ret.value) + """));
        }
    })
}

function isPromise(obj) {
    return "function" == typeof obj.then;
}

function toPromise(obj) {
    if (isPromise(obj)) return obj;
    if ("function" == typeof obj) return thunkToPromise(obj);
    return obj;
}

function thunkToPromise(fn) {
    return new Promise(function(resolve, reject) {
        fn(function(err, res) {
            if (err) return reject(err);
            resolve(res);
        });
    });
}

module.exports = run;
co

如果我們再將這個啟動器函數(shù)寫的完善一些,我們就相當(dāng)于寫了一個 co,實際上,上面的代碼確實是來自于 co……

而 co 是什么? co 是大神 TJ Holowaychuk 于 2013 年 6 月發(fā)布的一個小模塊,用于 Generator 函數(shù)的自動執(zhí)行。

如果直接使用 co 模塊,這兩種不同的例子可以簡寫為:

// yield 后是一個 Promise
var fetch = require("node-fetch");
var co = require("co");

function* gen() {
    var r1 = yield fetch("https://api.github.com/users/github");
    var json1 = yield r1.json();
    var r2 = yield fetch("https://api.github.com/users/github/followers");
    var json2 = yield r2.json();
    var r3 = yield fetch("https://api.github.com/users/github/repos");
    var json3 = yield r3.json();

    console.log([json1.bio, json2[0].login, json3[0].full_name].join("
"));
}

co(gen);
// yield 后是一個回調(diào)函數(shù)
var co = require("co");

function fetchData(url) {
    return function(cb) {
        setTimeout(function() {
            cb(null, { status: 200, data: url })
        }, 1000)
    }
}

function* gen() {
    var r1 = yield fetchData("https://api.github.com/users/github");
    var r2 = yield fetchData("https://api.github.com/users/github/followers");

    console.log([r1.data, r2.data].join("
"));
}

co(gen);

是不是特別的好用?

ES6 系列

ES6 系列目錄地址:https://github.com/mqyqingfeng/Blog

ES6 系列預(yù)計寫二十篇左右,旨在加深 ES6 部分知識點的理解,重點講解塊級作用域、標(biāo)簽?zāi)0濉⒓^函數(shù)、Symbol、Set、Map 以及 Promise 的模擬實現(xiàn)、模塊加載方案、異步處理等內(nèi)容。

如果有錯誤或者不嚴(yán)謹(jǐn)?shù)牡胤剑垊?wù)必給予指正,十分感謝。如果喜歡或者有所啟發(fā),歡迎 star,對作者也是一種鼓勵。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/98472.html

相關(guān)文章

  • 使用JavaScript ES6新特性計算Fibonacci(非波拉契數(shù)列)

    摘要:采用的生成非波拉契數(shù)列提供了原生的支持,語法非常有特色,關(guān)鍵字后面緊跟一個星號。的詳細(xì)介紹參考官網(wǎng)先看如何用這個黑科技重新實現(xiàn)非波拉契樹立的生成。在這個內(nèi)部,我們定義了一個無限循環(huán),用于計算非波拉契數(shù)列。 程序員面試系列 Java面試系列-webapp文件夾和WebContent文件夾的區(qū)別? 程序員面試系列:Spring MVC能響應(yīng)HTTP請求的原因? Java程序員面試系列-什么...

    yanbingyun1990 評論0 收藏0
  • ES6 系列 Babel 將 Async 編譯成了什么樣子

    摘要:大約后輸出我們直接在官網(wǎng)的粘貼上述代碼,然后查看代碼編譯成什么樣子相關(guān)的代碼我們在系列之將編譯成了什么樣子中已經(jīng)介紹過了,這次我們重點來看看函數(shù)以上這段代碼主要是用來實現(xiàn)的自動執(zhí)行以及返回。 前言 本文就是簡單介紹下 Async 語法編譯后的代碼。 Async const fetchData = (data) => new Promise((resolve) => setTimeout...

    wangym 評論0 收藏0
  • ES6 系列我們來聊聊 Async

    摘要:標(biāo)準(zhǔn)引入了函數(shù),使得異步操作變得更加方便。在異步處理上,函數(shù)就是函數(shù)的語法糖。在實際項目中,錯誤處理邏輯可能會很復(fù)雜,這會導(dǎo)致冗余的代碼。的出現(xiàn)使得就可以捕獲同步和異步的錯誤。如果有錯誤或者不嚴(yán)謹(jǐn)?shù)牡胤剑垊?wù)必給予指正,十分感謝。 async ES2017 標(biāo)準(zhǔn)引入了 async 函數(shù),使得異步操作變得更加方便。 在異步處理上,async 函數(shù)就是 Generator 函數(shù)的語法糖。 ...

    Songlcy 評論0 收藏0
  • ES6 系列 Babel 將 Generator 編譯成了什么樣子

    摘要:前言本文就是簡單介紹下語法編譯后的代碼。如果有錯誤或者不嚴(yán)謹(jǐn)?shù)牡胤剑垊?wù)必給予指正,十分感謝。如果喜歡或者有所啟發(fā),歡迎,對作者也是一種鼓勵。 前言 本文就是簡單介紹下 Generator 語法編譯后的代碼。 Generator function* helloWorldGenerator() { yield hello; yield world; return ending...

    EddieChan 評論0 收藏0
  • JavaScript異步編程終極演變

    摘要:在誕生以前,異步編程的方式大概有下面四種回調(diào)函數(shù)事件監(jiān)聽發(fā)布訂閱對象將異步編程帶入了一個全新的階段,中的函數(shù)更是給出了異步編程的終極解決方案。這意味著,出錯的代碼與處理錯誤的代碼,實現(xiàn)了時間和空間上的分離,這對于異步編程無疑是很重要的。 寫在前面 有一個有趣的問題: 為什么Node.js約定回調(diào)函數(shù)的第一個參數(shù)必須是錯誤對象err(如果沒有錯誤,該參數(shù)就是null)? 原因是執(zhí)行回調(diào)函...

    whjin 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<