亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專(zhuān)欄INFORMATION COLUMN

異步編程解決方案 - generator

AbnerMing / 826人閱讀

摘要:異步編程解決方案事件發(fā)布訂閱模式訂閱,發(fā)布事件監(jiān)聽(tīng)是一種高階函數(shù)的應(yīng)用,通過(guò)事件可以把內(nèi)部數(shù)據(jù)傳遞給外部的調(diào)用者,編程者可以不用關(guān)心組件內(nèi)部如何執(zhí)行,只需關(guān)注在需要的事件點(diǎn)上即可。

異步編程難點(diǎn) 異常處理

在處理異常時(shí)經(jīng)常用try/catch/final語(yǔ)句塊進(jìn)行異常捕獲,但是這種異常捕獲對(duì)異步編程并不是用

function async(callback) {
    process.nextTick(callback);
}

try {
    async(function () {
        console.log(a);
    });
} catch (err) {
    // TODO
}

異步代碼分為兩個(gè)過(guò)程,提交請(qǐng)求和處理結(jié)果,其中代碼在異步處理完成之前返回,而異常不一定在這個(gè)過(guò)程中發(fā)生,所以try、catch不會(huì)有任何作用,調(diào)用async時(shí),callback被暫時(shí)掛起,等到代碼執(zhí)行完畢才會(huì)執(zhí)行,try只能捕獲當(dāng)前事件循環(huán)的異常,對(duì)下一次的事件循環(huán)無(wú)法處理(nodejs異步時(shí)間做了約定,異常一定被當(dāng)成第一個(gè)參數(shù)傳回,在調(diào)用callback時(shí)先判斷是否有異常發(fā)生)

function async(callback) {
    process.nextTick(function () {
        if (err) {
            return callback(err);
        }
        callback(null);

    });
}

try {
    async(function (err) {
        if (!err) {
            console.log(a);
        }
    });
} catch (err) {
    // TODO
}
函數(shù)嵌套過(guò)深

對(duì)于Node和agax調(diào)用而言,有時(shí)會(huì)存在多個(gè)異步調(diào)用嵌套的場(chǎng)景,比如一個(gè)文件目錄的遍歷操作:

fs.readdir(path.join(__dirname, ".."), function (err, file) {
    files.forEach(function (filename, index) {
        fs.readFile(filename, "utf8), function (err, file) {
            // TODO
        }
    });
});

或者一個(gè)網(wǎng)頁(yè)渲染操作:

$(selector).click(function (e) {
    $ajax({
        data: "",
        success: function (data) {
            template.init(data, function (tpl) {
                // TODO
            });
        }
    });
});

上面的代碼邏輯上是沒(méi)有問(wèn)題的,但是并沒(méi)有利用好異步I/O帶來(lái)的優(yōu)勢(shì),這是異步編程的典型問(wèn)題。

多線(xiàn)程編程

如果是多核CPU,單個(gè)Node進(jìn)程實(shí)際沒(méi)有充分利用多核CPU,瀏覽器提出了Web workers,通過(guò)將javascrit執(zhí)行與UI渲染分離,可以良好的利用多核CPU。因?yàn)榍岸藶g覽器對(duì)標(biāo)準(zhǔn)的滯后,Web workers并沒(méi)有廣泛應(yīng)用起來(lái)。

異步轉(zhuǎn)同步

習(xí)慣同步編程的同學(xué),并不能從容面對(duì)異步編程帶來(lái)的副產(chǎn)品,比如嵌套回調(diào)、業(yè)務(wù)分散。Node 提供了絕大部分異步 API 卻很少有同步 API,往往出現(xiàn)同步需求會(huì)無(wú)所適從,雖然 Node 試圖異步轉(zhuǎn)同步但是并沒(méi)有原生的支持,需要借助庫(kù)或者編譯實(shí)現(xiàn),對(duì)于異步編程通過(guò)良好的流程控制,還是可以降落幾梳理成順序的形式。

異步編程解決方案 事件發(fā)布/訂閱模式
// 訂閱 
emiiiter.on("event", function(message) {
    console.log(message);
})
// 發(fā)布
emitter.emit("event", "i am a message");

事件監(jiān)聽(tīng)是一種高階函數(shù)的應(yīng)用,通過(guò)事件可以把內(nèi)部數(shù)據(jù)傳遞給外部的調(diào)用者,編程者可以不用關(guān)心組件內(nèi)部如何執(zhí)行,只需關(guān)注在需要的事件點(diǎn)上即可。注意:

如果事件的監(jiān)聽(tīng)器過(guò)多可能出現(xiàn)過(guò)度占用cup的結(jié)果。

如果運(yùn)行期間觸發(fā)了error事件,解釋器會(huì)檢查是否對(duì)error監(jiān)聽(tīng)了事件,如果有就交給監(jiān)聽(tīng)器處理,如果沒(méi)有則將錯(cuò)誤拋出。所以應(yīng)該對(duì)error事件做監(jiān)聽(tīng)。

利用事件可以解決雪崩問(wèn)題:當(dāng)大量的訪(fǎng)問(wèn)同時(shí)發(fā)生時(shí),服務(wù)器無(wú)法對(duì)所有的訪(fǎng)問(wèn)做處理,可以在第一個(gè)回調(diào)添加狀態(tài)鎖控制服務(wù)器的訪(fǎng)問(wèn)數(shù)量,同時(shí)使用事件(once)把所有請(qǐng)求壓入隊(duì)列中。

promise/deferred模式

promise/A 規(guī)定了三種狀態(tài),未完成態(tài)、完成態(tài)和失敗態(tài),未完成態(tài)向其他兩種狀態(tài)轉(zhuǎn)化,不能逆轉(zhuǎn);

pedding -> resolved

     -> rejected
function call(state, fn, err, arg) {
    if  (state === "pendding") {
        fn(arg);
    } else {
        fn(err);
    }

}
new Promise = function (fn) {
    this.state = "pendding";
    this.fn = function() {};
    return fn(this.resolve, this.reject);
}
Promise.prototype.then = function (fn) {
    this.fn = fn;
    return this;
}
Promise.prototype.resolve = function (arg) {
    this.state = "resolved";
    call(this.state, this.fn, null, arg);
    return this;
}
Promise.prototype.reject = function () {
    this.state = "rejected";
    var err = "err opened";
    call(this.state, this.fn, err);
    return this;
}
new Promise(function (resolve, reject) {
    setTimeout(function () {
        var value = "abc";
        resolve(value);
    }, 100);
}).then(function (result) {
    console.log(result);
});
流程控制庫(kù)
中間件

使用connect存儲(chǔ)中間件手動(dòng)調(diào)用執(zhí)行的方式,例如next,通常叫做尾觸發(fā),尾觸發(fā)在jquery中非常常見(jiàn),比如

$get("/get").success().error();

這種方式首先注冊(cè)中間件,每個(gè)中間件包括傳遞請(qǐng)求對(duì)象,響應(yīng)對(duì)象和尾觸發(fā)函數(shù),通過(guò)隊(duì)列行程一個(gè)處理流,最簡(jiǎn)單的中間例如:

function (req, res, err) {
    // 中間件
}

connect核心代碼:

function creatServer() {
    function app(req, res) {
        app.handle(req,res);
    }
    app.stack = [];
    for (var i = 0; i < arguments.length; ++i) {
        app.use(arguments[i]);
    }
    return app;
}

app.use:

app.use = function(router, fn) {
    this.stack.push(fn);
    return this;
}

next:

function handle = function() {
    // ...
    next();
}
function next() {
    // ... next callback ...
    layer = this.stack[index++];
    layer.handle(req, res, next);
}
async

異步的串行執(zhí)行

async.series([function (callback) {
    callback();
},function (callback) {
    callback();
}], function (err, result) {})

等價(jià)于:

function (callback) {
    function (callback) {
        callback();
    }
    callback();
}

異步的并行執(zhí)行:

async.parallel([function (callback) {
    callback();
}, function (callback) {
    callback();
}], function (err, results) {

});

等價(jià)于:

var counter = 2;
var results = [];
var done = function (index, value) {
    results[index] = value;
    if (!--conuter) {
        callback(null, results);
    }
}

function (callback) {
    // var value = ...
    callback();
    done(0, value);
}
function (callback) {
    // var value = ...
    callback();
    done(1, value);
}

依賴(lài)處理

當(dāng)前一個(gè)異步的結(jié)果是后一個(gè)異步的輸入時(shí),async使用waterfall方式處理

async.waterfall([function (callback) {
    callback();
}, function (arg1, callback) {
    callback();
}, function (arg2, callback) {
    callback();
}], function (err, results) {

});

當(dāng)存在很多依賴(lài)關(guān)系,有同步有異步時(shí),async使用auto()實(shí)現(xiàn)復(fù)雜的處理

async.waterfall({
    fun1:function (callback) {
        callback();
    },
    fun2: ["fun1", function (arg1, callback) {
        callback();
    }, function (arg2, callback) {
        callback();
    }]}, function (err, results) {

    });
step

step接受任意數(shù)量的任務(wù),所有任務(wù)會(huì)串行執(zhí)行:

step(task1, task2, task3);

step使用next把上一步的結(jié)果傳遞給下一步作為參數(shù)
在執(zhí)行多個(gè)異步任務(wù)時(shí),調(diào)用代碼如下:

step(function () {
    fn1(this.parallel());
    fn2(this.parallel());
}, function (err, result1, result2) {

});
wind

wind旨在控制異步流程的邏輯控制,其作用類(lèi)似generator:

eval(Wind.compile("async", funtion () {
    $await(Wind.Async.sleep(20)); //延遲20ms
    console.log("hello world");
}));
generator generaor函數(shù)
function * maker(){
    var index = 0;
    while (index < 10) {
        yield index++;
    }
}

var g = maker();
// 輸出結(jié)果
console.log(g.next().value); // 0
console.log(g.next().value); // 1
console.log(g.next().value); // 2
yeild關(guān)鍵字

yield 關(guān)鍵字用來(lái)暫停和恢復(fù)一個(gè)生成器函數(shù)

[rv] = yield [expression];

yield [[expression]];

rv 返回傳遞給生成器的 next() 方法的可選值,以恢復(fù)其執(zhí)行。

Regenerator

上面這段代碼等價(jià)下面代碼:

var _marked = [maker].map(regeneratorRuntime.mark);

function maker() {
    var index;
    return regeneratorRuntime.wrap(function maker$(_context) {
        while (1) {
            switch (_context.prev = _context.next) {
                case 0:
                    index = 0;

                case 1:
                    if (!(index < 10)) {
                        _context.next = 6;
                        break;
                    }

                    _context.next = 4;
                    return index++;

                case 4:
                    _context.next = 1;
                    break;

                case 6:
                case "end":
                    return _context.stop();
            }
        }
    }, _marked[0], this);
}

var g = maker();

console.log(g.next().value); // 0
console.log(g.next().value); // 1
console.log(g.next().value); // 2

編譯機(jī)制造了一個(gè)狀態(tài)機(jī),通過(guò)_context.next狀態(tài)的裝換完成代碼執(zhí)行的掛起。
假設(shè)狀態(tài)是0 -> n(n是最后一個(gè)狀態(tài))
0運(yùn)行第一個(gè)yield之前的所有代碼,n運(yùn)行最后一個(gè)yield函數(shù)之后的所有代碼,generator的next尾調(diào)用通過(guò)一個(gè)while循環(huán)實(shí)現(xiàn),如果_context.next到達(dá)最后一個(gè)case就退出循環(huán),等待下一次next調(diào)用

regenerator是用來(lái)生成generetor函數(shù)并返回一個(gè)迭代器供外界調(diào)用的高階函數(shù),功能主要是

regenerator-transform: 重寫(xiě)generator函數(shù)把yield重寫(xiě)成switch case,并且創(chuàng)建_context.next保存上下文環(huán)境;

包裝generator函數(shù)被返回一個(gè)迭代器對(duì)象;

經(jīng)過(guò)wrap返回的迭代器:

GeneratorFunctionPrototype {
    _invoke: function invoke(method, arg) { … }
    __proto__: GeneratorFunctionPrototype {
        constructor: function GeneratorFunctionPrototype() {},
        next: function (arg) { … },
        throw: function (arg) { … } 
        …
    }
}

當(dāng)調(diào)用迭代器對(duì)象iter.next()方法時(shí),因?yàn)橛腥缦麓a,所以會(huì)執(zhí)行_invoke方法,而根據(jù)前面wrap方法代碼可知,最終是調(diào)用了迭代器對(duì)象的 makeInvokeMethod (innerFn, self, context); 方法

makeInvokeMethod方法內(nèi)容較多,這里選取部分分析。

function makeInvokeMethod(innerFn, self, context) {
    var state = GenStateSuspendedStart;
    return function invoke(method, arg) {

makeInvokeMethod返回invoke函數(shù),當(dāng)我們執(zhí)行.next方法時(shí),實(shí)際調(diào)用的是invoke方法中的下面語(yǔ)句

var record = tryCatch(innerFn, self, context);

這里tryCatch方法中fn為經(jīng)過(guò)轉(zhuǎn)換后的example$方法,arg為上下文對(duì)象context,因?yàn)閕nvoke函數(shù)內(nèi)部對(duì)context的引用形成閉包引用,所以context上下文得以在迭代期間一直保持。

function tryCatch(fn, obj, arg) {
    try {
        return { type: "normal", arg: fn.call(obj, arg) };
    } catch (err) {
        return { type: "throw", arg: err };
    }
}

tryCatch方法會(huì)實(shí)際調(diào)用 example$ 方法,進(jìn)入轉(zhuǎn)換后的switch case,執(zhí)行代碼邏輯。如果得到的結(jié)果是一個(gè)普通類(lèi)型的值,我們將它包裝成一個(gè)可迭代對(duì)象格式,并且更新生成器狀態(tài)至GenStateCompleted或者GenStateSuspendedYield

var record = tryCatch(innerFn, self, context);
        if (record.type === "normal") {
        // If an exception is thrown from innerFn, we leave state ===
        // GenStateExecuting and loop back for another invocation.
        state = context.done
            ? GenStateCompleted
            : GenStateSuspendedYield;

        var info = {
            value: record.arg,
            done: context.done
        };

偽代碼:

function wrap(innerFn, outerFn, self, tryLocsList) {
    var protoGenerator = outerFn && outerFn.prototype instanceof Generator
        ? outerFn
        : Generator;
    var generator = Object.create(protoGenerator.prototype);
    var context = new Context(tryLocsList || []);

    generator._invoke = makeInvokeMethod(innerFn, self, context);

    return generator;
}

function makeInvokeMethod(innerFn, self, context) {
    var obj = this;
    return function invoke(method, arg) {
        context.method = method;
        // 把next帶入的arg參數(shù)賦值給sent
        if (context.method === "next") {
            context.sent = context._sent = context.arg;
        }
        // 實(shí)際上調(diào)用了mark$,并且?guī)肓薱ontext
        var record = {
            arg: innerFn.call(obj, context)
        };
        // 返回一個(gè)可以迭代的對(duì)象
        return {value: record.arg, done: context.done};
    };
}

// 用一個(gè)next調(diào)用invoke, 如果要進(jìn)行下一步就傳入next
generator.next = next(arg) {
    generator._invoke("next", arg);
}
cojs處理generator過(guò)程
thunk函數(shù)

能夠得到一個(gè)函數(shù)的函數(shù)叫thunk函數(shù), thunk函數(shù)是一個(gè)偏函數(shù),它只帶一個(gè)執(zhí)行參數(shù)

function getThunk(number) {
    return function (fn) {
        setTimeout(() => {
            if (number) {
                fn(null, number);
            } else {
                const err = "error open";
                fn(err);
            }
        }, number)
    }
}
cojs-generator的自動(dòng)執(zhí)行器
import co from "co";

co(function * () {
    var a = yield getThunk(100);
    var b = yield getThunk(1000);
    console.log("a:", a);
    console.log("b:", b);
    return [a, b];
})

// 輸出
// a 100
// b 1000
cojs代碼解析
function co2Thunk(fn) {
    return (done) => {
        const ctx = this;
        const g = fn.call(ctx);
        function next(err, res) {
            console.log("next1", res);
            let it = g.next(res);
            if (it.done) {
                done.call(ctx, err, it.value);
            } else {
                it.value(next);
            }
        }
        next();
    }
}

co2Thunk(function * () {
    var a = yield getThunk(10000);
    var b = yield getThunk(1000);
    // console.log("a:", a);
    // console.log("b:", b);
    return [a, b];
})(function (err, args) {
    console.log("callback thunk co : ==========");
    // console.log(err, args);
});

co2Thunk的代碼等價(jià)于:

function co2Thunk(fn) {
    return (done) => {
        const ctx = this;
        const g = fn.call(ctx);
        let it0 = g.next();
        it0.value((err, res) => {
            const it1 = g.next(res); // 第一次迭代返回的是getThunk(10000);
            it0.value((err, res) => {
                const it1 = g.next(res); // 第二次迭代返回的是getThunk(1000);
                it1.value((err, res) => {
                    const it2 = g.next(data);
                    // ...
                });
            });
        });
    }
}

// it.value 等價(jià)于:
function (fn) {
    setTimeout(() => {
        if (number) {
            fn(null, number);
        } else {
            const err = "error open";
            fn(err);
        }
    }, number)
}
promise版
function co2Promise(fn) {
    return new Promise((resolve, reject) => {
        const ctx = this;
        const g = fn.call(ctx);
        function next(err, res) {
            let it = g.next(res);
            if (it.done) {
                resolve(it.value);
            } else {
                it.value(next);
            }
        }
        next();
    });
}

co2Promise(function * () {
    var a = yield getThunk(100);
    var b = yield getThunk(1000);
    console.log("a:", a);
    console.log("b:", b);
    return [a, b];
}).then(function (args) {
    console.log("callback promise co : ==========");
    console.log(args);
});
thunk升級(jí)版
function co2Thunk(fn) {
    return (done) => {
        const ctx = this;
        const g = fn.call(ctx);
        function next(err, res) {
            let it = g.next(res);
            if (it.done) {
                done.call(ctx, err, it.value);
            } else {
                // 增加對(duì)其他類(lèi)型的處理
                const value = toThunk.call(ctx, it.value);
                // 對(duì)于promise 此處應(yīng)該是 value.then(next)
                value(next);
            }
        }
        next();
    }
}

co2Thunk(function * () {
    var a = getThunk(100);
    var b = getThunk(1000);
    // console.log("a:", a); console.log("b:", b);
    return yield [a, b];
})(function (err, args) {
    console.log("callback thunk co : ==========");
    console.log(err, args);
});

function toThunk(obj) {
    if (isObject(obj) || isArray(obj)) {
        return objectToThunk(obj);
    }
    if (isPromise(obj)) {
        return promiseToThunk.call(ctx, obj);
    }
    return obj;
}

function objectToThunk(obj) {
    return function (done) {
        let keys = Object.keys(obj);
        let length = keys.length;
        let results = new obj.constructor();
        for(let key in keys) {
            const fn = toThunk(obj[key]);
            fn((err, res) => {
                results[key] = res;
                --length || done(null, results);
            }, key);
        }
    }
}

function promiseToThunk(promise){
    return function(done){
        promise.then(function(err,res){
            done(err,res);
        },done)
    }
}

function isObject(obj) {
    return obj && Object == obj.constructor;
}
function isArray(obj) {
    return Array.isArray(obj);
}

function isPromise(obj) {
return obj && "function" == typeof obj.then;
}
async/await
async function fn(args){
// ...
}

等同于

function fn(args){ 
    return co2Thunk(function*() {
        // ...
    }); 
}

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/84625.html

Failed to recv the data from server completely (SIZE:0/8, REASON:closed)