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

資訊專(zhuān)欄INFORMATION COLUMN

Javascript異步編程 - 函數(shù)式編程 - Javascript核心

hlcc / 697人閱讀

摘要:不少第三方模塊并沒(méi)有做到異步調(diào)用,卻裝作支持回調(diào),堆棧的風(fēng)險(xiǎn)就更大。我們可以編寫(xiě)一個(gè)高階函數(shù),讓傳入的函數(shù)順序執(zhí)行還是我們之前的例子看起來(lái)還是很不錯(cuò)的,簡(jiǎn)潔并且清晰,最終的代碼量也沒(méi)有增加。

  

原文: http://pij.robinqu.me/JavaScript_Core/Functional_JavaScript/Async_Programing_In_JavaScript.html

  

源代碼: https://github.com/RobinQu/Programing-In-JavaScript/blob/master/chapters/JavaScript_Core/Functional_JavaScript/Async_Programing_In_JavaScript.md

本文需要補(bǔ)充更多例子

本文存在批注,但該網(wǎng)站的Markdown編輯器不支持,所以無(wú)法正常展示,請(qǐng)到原文參考。

Async Programing in Javascript

本文從異步風(fēng)格講起,分析Javascript中異步變成的技巧、問(wèn)題和解決方案。具體的,從回調(diào)造成的問(wèn)題說(shuō)起,并談到了利用事件、Promise、Generator等技術(shù)來(lái)解決這些問(wèn)題。

異步之殤 non-blocking無(wú)限好?

異步,是沒(méi)有線程模型的Javascript的救命稻草。說(shuō)得高大上一些,就是運(yùn)用了Reactor設(shè)計(jì)模式1

Javascript的一切都是圍繞著“異步”二子的。無(wú)論是瀏覽器環(huán)境,還是node環(huán)境,大多數(shù)API都是通過(guò)“事件”來(lái)將請(qǐng)求(或消息、調(diào)用)和返回值(或結(jié)果)分離。而“事件”,都離不開(kāi)回調(diào)(Callback),例如,

var fs = require("fs");
fs.readFile(__filename, function(e, data) {
    console.log("2. in callback");
});
console.log("1. after invoke");

fs模塊封裝了復(fù)雜的IO模塊,其調(diào)用結(jié)果是通過(guò)一個(gè)簡(jiǎn)單的callback告訴調(diào)用者的。看起來(lái)是十分不錯(cuò)的,我們看看Ruby的EventMachine

require "em-files"

EM::run do
  EM::File::open(__FILE__, "r") do |io|
    io.read(1024) do |data|
      puts data
      io.close
    end
    EM::stop
  end
end

由于Ruby的標(biāo)準(zhǔn)庫(kù)里面的API全是同步的,異步的只有類(lèi)似EventMachine這樣的第三方API才能提供支持。實(shí)際風(fēng)格上,兩者類(lèi)似,就我們這個(gè)例子來(lái)說(shuō),Javascript的版本似乎更加簡(jiǎn)介,而且不需要添加額外的第三方模塊。

異步模式,相比線程模式,損耗更小,在部分場(chǎng)景性能甚至比Java更好2。并且,non-blocking的API是node默認(rèn)的,這使nodejs和它的異步回調(diào)大量應(yīng)用。

例如,我們想要找到當(dāng)前目錄中所有文件的尺寸:

fs.readdir(__dirname, function(e, files) {//callback 1
    if(e) {
        return console.log(e);
    }
    dirs.forEach(function(file) {//callback 2
        fs.stat(file, function(e, stats) {//callback 3
            if(e) {
                return console.log(e);
            }
            if(stats.isFile()) {
                console.log(stats.size);
            }
        });
    });
});

非常簡(jiǎn)單的一個(gè)任務(wù)便造成了3層回調(diào)。在node應(yīng)用爆發(fā)的初期,大量的應(yīng)用都是在這樣的風(fēng)格中誕生的。顯然,這樣的代碼風(fēng)格有如下風(fēng)險(xiǎn):

代碼難以閱讀、維護(hù):嵌套多層回調(diào)之后,作者自己都不清楚函數(shù)層次了。

潛在的調(diào)用堆棧消耗:Javascript中,遠(yuǎn)比你想像的簡(jiǎn)單去超出最大堆棧。不少第三方模塊并沒(méi)有做到異步調(diào)用,卻裝作支持回調(diào),堆棧的風(fēng)險(xiǎn)就更大。

還想更遭么?前兩條就夠了……

不少程序員,因?yàn)榈谝粭l而放棄nodejs,甚至放棄Javascript。而關(guān)于第二條,各種隱性bug的排除和性能損耗的優(yōu)化工作在向程序員招手。

等等,你說(shuō)我一直再說(shuō)node,沒(méi)有提及瀏覽器中的情況?我們來(lái)看個(gè)例子:

/*glboal $ */
// we have jquery in the `window`
$("#sexyButton").on("click", function(data) {//callback 1
    $.getJSON("/api/topcis", function(data) {//callback 2
        var list = data.topics.map(function(t) {
            return t.id + ". " + t.title + "
";
        });
        var id = confirm("which topcis are you interested in? Select by ID : " + list);
        $.getJSON("/api/topics/" + id, function(data) {//callback 3
            alert("Detail topic: " + data.content);
        });
    });

});

我們嘗試獲取一個(gè)文章列表,然后給予用戶一些交互,讓用戶選擇希望詳細(xì)了解的一個(gè)文章,并繼續(xù)獲取文章詳情。這個(gè)簡(jiǎn)單的例子,產(chǎn)生了3個(gè)回調(diào)。

事實(shí)上,異步的性質(zhì)是Javascript語(yǔ)言本身的固有風(fēng)格,跟宿主環(huán)境無(wú)關(guān)。所以,回調(diào)漫天飛造成的問(wèn)題是Javascript語(yǔ)言的共性。

解決方案 Evented

Javascript程序員也許是最有創(chuàng)造力的一群程序員之一。對(duì)于回調(diào)問(wèn)題,最終有了很多解決方案。最自然想到的,便是利用事件機(jī)制。

還是之前加載文章的場(chǎng)景:

var TopicController = new EventEmitter();

TopicController.list = function() {//a simple wrap for ajax request
    $.getJSON("/api/topics", this.notify("topic:list"));
    return this;
};

TopicController.show = function(id) {//a simple wrap for ajax request
    $.getJSON("/api/topics/" + id, this.notify("topic:show", id));
    return this;
};

TopicController.bind = function() {//bind DOM events
    $("#sexyButton").on("click", this.run.bind(this));
    return this;
};

TopicController._queryTopic = function(data) {
    var list = data.topics.map(function(t) {
        return t.id + ". " + t.title + "
";
    });
    var id = confirm("which topcis are you interested in? Select by ID : " + list);
    this.show(id).listenTo("topic:show", this._showTopic);
};

TopicController._showTopic = function(data) {
    alert(data.content);
};

TopicController.listenTo = function(eventName, listener) {//a helper method to `bind`
    this.on(eventName, listener.bind(this));
};

TopicController.notify = function(eventName) {//generate a notify callback internally
    var self = this, args;
    args = Array.prototype.slice(arguments, 1);
    return function(data) {
        args.unshift(data);
        args.unshift(eventName);
        self.emit.apply(self, args);
    };
};

TopicController.run = function() {
    this.list().lisenTo("topic:list", this._queryTopic);
};

// kickoff
$(function() {
    TopicController.run();
});

可以看到,現(xiàn)在這種寫(xiě)法B格就高了很多。各種封裝、各種解藕。首先,除了萬(wàn)能的jQuery,我們還依賴EventEmitter,這是一個(gè)觀察者模式的實(shí)現(xiàn)3,比如asyncly/EventEmitter2。簡(jiǎn)單的概括一下這種風(fēng)格:

杜絕了大部分將匿名函數(shù)用作回調(diào)的場(chǎng)景,達(dá)到零嵌套,代碼簡(jiǎn)介明了

每個(gè)狀態(tài)(或步驟)之間,利用事件機(jī)制進(jìn)行關(guān)聯(lián)

每個(gè)步驟都相互獨(dú)立,方便日后維護(hù)

如果你硬要挑剔的話,也有缺點(diǎn);

由于過(guò)度分離,整體流程模糊

代碼量激增,又加大了另一種維護(hù)成本

高階函數(shù)

利用高階函數(shù),可以順序、并發(fā)的將函數(shù)遞歸執(zhí)行。

我們可以編寫(xiě)一個(gè)高階函數(shù),讓傳入的函數(shù)順序執(zhí)行:

var runInSeries = function(ops, done) {
    var i = 0, next;
    next = function(e) {
        if(e) {
            return done(e);
        }
        var args = Array.prototype.slice.call(arguments, 1);
        args.push(next);
        ops[0].apply(null, args);
    };
    next();
};

還是我們之前的例子:

var list = function(next) {
    $.getJSON("/api/topics", function(data) { next(null, data); });
};

var query = function(data, next) {
    var list = data.topics.map(function(t) {
        return t.id + ". " + t.title + "
";
    });
    var id = confirm("which topcis are you interested in? Select by ID : " + list);
    next(null, id);
};

var show = function(id, next) {
    $.getJSON("/api/topics/" + id, function(data) { next(null, data); });
};

$("#sexyButton").on("click", function() {
    runInSeries([list, query, show], function(e, detail) {
        alert(detail);
    });
});

看起來(lái)還是很不錯(cuò)的,簡(jiǎn)潔并且清晰,最終的代碼量也沒(méi)有增加。如果你喜歡這種方式,去看一下caolan/async會(huì)發(fā)現(xiàn)更多精彩。

Promise
  

A promise represents the eventual result of an asynchronous operation. The primary way of interacting with a promise is through its then method, which registers callbacks to receive either a promise’s eventual value or the reason why the promise cannot be fulfilled.

除開(kāi)文縐縐的解釋?zhuān)琍romise是一種對(duì)一個(gè)任務(wù)的抽象。Promise的相關(guān)API提供了一組方法和對(duì)象來(lái)實(shí)現(xiàn)這種抽象。

Promise的實(shí)現(xiàn)目前有很多:

ECMAScript Promise4

即原生的Promise對(duì)象, Chrome32+以上支持

Promise/A+5標(biāo)準(zhǔn)

kriskowal/q

cujojs/when

tildeio/rsvp.js

其他廠商標(biāo)準(zhǔn)

jQuery.Deferred

WinJS

雖然標(biāo)準(zhǔn)很多,但是所有的實(shí)現(xiàn)基本遵循如下基本規(guī)律:

Promise對(duì)象

是一個(gè)有限狀態(tài)機(jī)

完成(fulfilled)

否定(rejected)

等待(pending)

結(jié)束(settled)

一定會(huì)有一個(gè)then([fulfill], [reject])方法,讓使用者分別處理成功失敗

可選的done([fn])fail([fn])方法

支持鏈?zhǔn)紸PI

Deffered對(duì)象

提供rejectresolve方法,來(lái)完成一個(gè)Promise

筆者會(huì)在專(zhuān)門(mén)的文章內(nèi)介紹Promise的具體機(jī)制和實(shí)現(xiàn)。在這里僅淺嘗輒止,利用基本隨處可得的jQuery來(lái)解決之前的那個(gè)小場(chǎng)景中的異步問(wèn)題:

$("#sexyButton").on("click", function(data) {
    $.getJSON("/api/topcis").done(function(data) {
        var list = data.topics.map(function(t) {
            return t.id + ". " + t.title + "
";
        });
        var id = confirm("which topcis are you interested in? Select by ID : " + list);
        $.getJSON("/api/topics/" + id).done(function(done) {
            alert("Detail topic: " + data.content);
        });
    });
});

很遺憾,使用Promise并沒(méi)有讓回調(diào)的問(wèn)題好多少。在這個(gè)場(chǎng)景,Promise的并沒(méi)有體現(xiàn)出它的強(qiáng)大之處。我們把jQuery官方文檔中的例子拿出來(lái)看看:

$.when( $.ajax( "/page1.php" ), $.ajax( "/page2.php" ) ).done(function( a1, a2 ) {
  // a1 and a2 are arguments resolved for the page1 and page2 ajax requests, respectively.
  // Each argument is an array with the following structure: [ data, statusText, jqXHR ]
  var data = a1[ 0 ] + a2[ 0 ]; // a1[ 0 ] = "Whip", a2[ 0 ] = " It"
  if ( /Whip It/.test( data ) ) {
    alert( "We got what we came for!" );
  }
});

這里,同時(shí)發(fā)起了兩個(gè)AJAX請(qǐng)求,并且將這兩個(gè)Promise合并成一個(gè),開(kāi)發(fā)者只用處理這最終的一個(gè)Promise。

例如Q.jswhen.js的第三方庫(kù),可以支持更多復(fù)雜的特性。也會(huì)讓你的代碼風(fēng)格大為改觀。可以說(shuō),Promise為處理復(fù)雜流程開(kāi)啟了新的大門(mén),但是也是有成本的。這些復(fù)雜的封裝,都有相當(dāng)大的開(kāi)銷(xiāo)6

Geneartor

ES6的Generator引入的yield表達(dá)式,讓流程控制更加多變。node-fiber讓我們看到了coroutine在Javascript中的樣子。

var Fiber = require("fibers");

function sleep(ms) {
    var fiber = Fiber.current;
    setTimeout(function() {
        fiber.run();
    }, ms);
    Fiber.yield();
}

Fiber(function() {
    console.log("wait... " + new Date);
    sleep(1000);
    console.log("ok... " + new Date);
}).run();
console.log("back in main");

但想象一下,如果每個(gè)Javascript都有這個(gè)功能,那么一個(gè)正常Javascript程序員的各種嘗試就會(huì)被挑戰(zhàn)。你的對(duì)象會(huì)莫名其妙的被另外一個(gè)fiber中的代碼更改。

也就是說(shuō),還沒(méi)有一種語(yǔ)法設(shè)計(jì)能讓支持fiber和不支持fiber的Javascript代碼混用并且不造成混淆。node-fiber的這種不可移植性,讓coroutine在Javascript中并不那么現(xiàn)實(shí)7

但是yield是一種Shallow coroutines,它只能停止用戶代碼,并且只有在GeneratorFunction才可以用yield

筆者在另外一篇文章中已經(jīng)詳細(xì)介紹了如何利用Geneator來(lái)解決異步流程的問(wèn)題。

利用yield實(shí)現(xiàn)的suspend方法,可以讓我們之前的問(wèn)題解決的非常簡(jiǎn)介:

$("#sexyButton").on("click", function(data) {
    suspend(function *() {
        var data = yield $.getJSON("/api/topcis");
        var list = data.topics.map(function(t) {
            return t.id + ". " + t.title + "
";
        });
        var id = confirm("which topcis are you interested in? Select by ID : " + list);
        var detail = yield $.getJSON("/api/topics/");
        alert("Detail topic: " + detail.content);
    })();
});

為了利用yield,我們也是有取舍的:

Generator的兼容性并不好,僅有新版的node和Chrome支持

需要大量重寫(xiě)基礎(chǔ)框架,是接口規(guī)范化(thunkify),來(lái)支持yield的一些約束

yield所產(chǎn)生的代碼風(fēng)格,可能對(duì)部分新手造成迷惑

多層yield所產(chǎn)生堆棧及其難以調(diào)試

結(jié)語(yǔ)

說(shuō)了這么多,異步編程這種和線程模型迥然不同的并發(fā)處理方式,隨著node的流行也讓更多程序員了解其與眾不同的魅力。如果下次再有C或者Java程序員說(shuō),Javascript的回調(diào)太難看,請(qǐng)讓他好好讀一下這篇文章吧!

http://en.wikipedia.org/wiki/Reactor_pattern??

http://strongloop.com/strongblog/node-js-is-faster-than-java/??

en.wikipedia.org/wiki/Observer_pattern??

http://wiki.ecmascript.org/doku.php?id=strawman:concurrency??

http://promises-aplus.github.io/promises-spec/??

http://thanpol.as/javascript/promises-a-performance-hits-you-should-be-aware-of/??

http://calculist.org/blog/2011/12/14/why-coroutines-wont-work-on-the-web/??

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/78132.html

相關(guān)文章

  • 走進(jìn)JavaScript響應(yīng)編程(Reactive Programming)

    摘要:補(bǔ)充說(shuō)明響應(yīng)式編程采用了訂閱觀察者設(shè)計(jì)模式,使訂閱者可以將通知主動(dòng)發(fā)送給各訂閱者。一個(gè)響應(yīng)式編程的實(shí)現(xiàn)庫(kù)是一個(gè)庫(kù),它通過(guò)使用序列來(lái)編寫(xiě)異步和基于事件的程序。 或許響應(yīng)式布局這個(gè)名單大家都聽(tīng)過(guò)或者都自己實(shí)現(xiàn)過(guò),那么響應(yīng)式編程是什么呢?下面我們來(lái)具體聊一聊。 我的理解 從字面意思上我們可以大致理解為:所有的事件存在于一條事件總線上,所有的事件都可以看作未來(lái)某個(gè)時(shí)間將要發(fā)生的事件流(stre...

    bovenson 評(píng)論0 收藏0
  • 雙十二大前端工程師讀書(shū)清單

    摘要:本文最早為雙十一而作,原標(biāo)題雙大前端工程師讀書(shū)清單,以付費(fèi)的形式發(fā)布在上。發(fā)布完本次預(yù)告后,捕捉到了一個(gè)友善的吐槽讀書(shū)清單也要收費(fèi)。這本書(shū)便從的異步編程講起,幫助我們?cè)O(shè)計(jì)快速響應(yīng)的網(wǎng)絡(luò)應(yīng)用,而非簡(jiǎn)單的頁(yè)面。 本文最早為雙十一而作,原標(biāo)題雙 11 大前端工程師讀書(shū)清單,以付費(fèi)的形式發(fā)布在 GitChat 上。發(fā)布之后在讀者圈群聊中和讀者進(jìn)行了深入的交流,現(xiàn)免費(fèi)分享到這里,不足之處歡迎指教...

    happen 評(píng)論0 收藏0
  • 雙十二大前端工程師讀書(shū)清單

    摘要:本文最早為雙十一而作,原標(biāo)題雙大前端工程師讀書(shū)清單,以付費(fèi)的形式發(fā)布在上。發(fā)布完本次預(yù)告后,捕捉到了一個(gè)友善的吐槽讀書(shū)清單也要收費(fèi)。這本書(shū)便從的異步編程講起,幫助我們?cè)O(shè)計(jì)快速響應(yīng)的網(wǎng)絡(luò)應(yīng)用,而非簡(jiǎn)單的頁(yè)面。 本文最早為雙十一而作,原標(biāo)題雙 11 大前端工程師讀書(shū)清單,以付費(fèi)的形式發(fā)布在 GitChat 上。發(fā)布之后在讀者圈群聊中和讀者進(jìn)行了深入的交流,現(xiàn)免費(fèi)分享到這里,不足之處歡迎指教...

    余學(xué)文 評(píng)論0 收藏0
  • 雙十二大前端工程師讀書(shū)清單

    摘要:本文最早為雙十一而作,原標(biāo)題雙大前端工程師讀書(shū)清單,以付費(fèi)的形式發(fā)布在上。發(fā)布完本次預(yù)告后,捕捉到了一個(gè)友善的吐槽讀書(shū)清單也要收費(fèi)。這本書(shū)便從的異步編程講起,幫助我們?cè)O(shè)計(jì)快速響應(yīng)的網(wǎng)絡(luò)應(yīng)用,而非簡(jiǎn)單的頁(yè)面。 本文最早為雙十一而作,原標(biāo)題雙 11 大前端工程師讀書(shū)清單,以付費(fèi)的形式發(fā)布在 GitChat 上。發(fā)布之后在讀者圈群聊中和讀者進(jìn)行了深入的交流,現(xiàn)免費(fèi)分享到這里,不足之處歡迎指教...

    Jochen 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<