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

資訊專欄INFORMATION COLUMN

Javascript異步編程 - 函數式編程 - Javascript核心

hlcc / 691人閱讀

摘要:不少第三方模塊并沒有做到異步調用,卻裝作支持回調,堆棧的風險就更大。我們可以編寫一個高階函數,讓傳入的函數順序執行還是我們之前的例子看起來還是很不錯的,簡潔并且清晰,最終的代碼量也沒有增加。

  

原文: 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

本文需要補充更多例子

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

Async Programing in Javascript

本文從異步風格講起,分析Javascript中異步變成的技巧、問題和解決方案。具體的,從回調造成的問題說起,并談到了利用事件、Promise、Generator等技術來解決這些問題。

異步之殤 non-blocking無限好?

異步,是沒有線程模型的Javascript的救命稻草。說得高大上一些,就是運用了Reactor設計模式1

Javascript的一切都是圍繞著“異步”二子的。無論是瀏覽器環境,還是node環境,大多數API都是通過“事件”來將請求(或消息、調用)和返回值(或結果)分離。而“事件”,都離不開回調(Callback),例如,

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

fs模塊封裝了復雜的IO模塊,其調用結果是通過一個簡單的callback告訴調用者的。看起來是十分不錯的,我們看看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的標準庫里面的API全是同步的,異步的只有類似EventMachine這樣的第三方API才能提供支持。實際風格上,兩者類似,就我們這個例子來說,Javascript的版本似乎更加簡介,而且不需要添加額外的第三方模塊。

異步模式,相比線程模式,損耗更小,在部分場景性能甚至比Java更好2。并且,non-blocking的API是node默認的,這使nodejs和它的異步回調大量應用。

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

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);
            }
        });
    });
});

非常簡單的一個任務便造成了3層回調。在node應用爆發的初期,大量的應用都是在這樣的風格中誕生的。顯然,這樣的代碼風格有如下風險:

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

潛在的調用堆棧消耗:Javascript中,遠比你想像的簡單去超出最大堆棧。不少第三方模塊并沒有做到異步調用,卻裝作支持回調,堆棧的風險就更大。

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

不少程序員,因為第一條而放棄nodejs,甚至放棄Javascript。而關于第二條,各種隱性bug的排除和性能損耗的優化工作在向程序員招手。

等等,你說我一直再說node,沒有提及瀏覽器中的情況?我們來看個例子:

/*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);
        });
    });

});

我們嘗試獲取一個文章列表,然后給予用戶一些交互,讓用戶選擇希望詳細了解的一個文章,并繼續獲取文章詳情。這個簡單的例子,產生了3個回調。

事實上,異步的性質是Javascript語言本身的固有風格,跟宿主環境無關。所以,回調漫天飛造成的問題是Javascript語言的共性。

解決方案 Evented

Javascript程序員也許是最有創造力的一群程序員之一。對于回調問題,最終有了很多解決方案。最自然想到的,便是利用事件機制。

還是之前加載文章的場景:

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();
});

可以看到,現在這種寫法B格就高了很多。各種封裝、各種解藕。首先,除了萬能的jQuery,我們還依賴EventEmitter,這是一個觀察者模式的實現3,比如asyncly/EventEmitter2。簡單的概括一下這種風格:

杜絕了大部分將匿名函數用作回調的場景,達到零嵌套,代碼簡介明了

每個狀態(或步驟)之間,利用事件機制進行關聯

每個步驟都相互獨立,方便日后維護

如果你硬要挑剔的話,也有缺點;

由于過度分離,整體流程模糊

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

高階函數

利用高階函數,可以順序、并發的將函數遞歸執行。

我們可以編寫一個高階函數,讓傳入的函數順序執行:

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);
    });
});

看起來還是很不錯的,簡潔并且清晰,最終的代碼量也沒有增加。如果你喜歡這種方式,去看一下caolan/async會發現更多精彩。

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.

除開文縐縐的解釋,Promise是一種對一個任務的抽象。Promise的相關API提供了一組方法和對象來實現這種抽象。

Promise的實現目前有很多:

ECMAScript Promise4

即原生的Promise對象, Chrome32+以上支持

Promise/A+5標準

kriskowal/q

cujojs/when

tildeio/rsvp.js

其他廠商標準

jQuery.Deferred

WinJS

雖然標準很多,但是所有的實現基本遵循如下基本規律:

Promise對象

是一個有限狀態機

完成(fulfilled)

否定(rejected)

等待(pending)

結束(settled)

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

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

支持鏈式API

Deffered對象

提供rejectresolve方法,來完成一個Promise

筆者會在專門的文章內介紹Promise的具體機制和實現。在這里僅淺嘗輒止,利用基本隨處可得的jQuery來解決之前的那個小場景中的異步問題:

$("#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并沒有讓回調的問題好多少。在這個場景,Promise的并沒有體現出它的強大之處。我們把jQuery官方文檔中的例子拿出來看看:

$.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!" );
  }
});

這里,同時發起了兩個AJAX請求,并且將這兩個Promise合并成一個,開發者只用處理這最終的一個Promise。

例如Q.jswhen.js的第三方庫,可以支持更多復雜的特性。也會讓你的代碼風格大為改觀。可以說,Promise為處理復雜流程開啟了新的大門,但是也是有成本的。這些復雜的封裝,都有相當大的開銷6

Geneartor

ES6的Generator引入的yield表達式,讓流程控制更加多變。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");

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

也就是說,還沒有一種語法設計能讓支持fiber和不支持fiber的Javascript代碼混用并且不造成混淆。node-fiber的這種不可移植性,讓coroutine在Javascript中并不那么現實7

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

筆者在另外一篇文章中已經詳細介紹了如何利用Geneator來解決異步流程的問題。

利用yield實現的suspend方法,可以讓我們之前的問題解決的非常簡介:

$("#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支持

需要大量重寫基礎框架,是接口規范化(thunkify),來支持yield的一些約束

yield所產生的代碼風格,可能對部分新手造成迷惑

多層yield所產生堆棧及其難以調試

結語

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

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/??

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/78132.html

相關文章

  • 走進JavaScript響應編程(Reactive Programming)

    摘要:補充說明響應式編程采用了訂閱觀察者設計模式,使訂閱者可以將通知主動發送給各訂閱者。一個響應式編程的實現庫是一個庫,它通過使用序列來編寫異步和基于事件的程序。 或許響應式布局這個名單大家都聽過或者都自己實現過,那么響應式編程是什么呢?下面我們來具體聊一聊。 我的理解 從字面意思上我們可以大致理解為:所有的事件存在于一條事件總線上,所有的事件都可以看作未來某個時間將要發生的事件流(stre...

    bovenson 評論0 收藏0
  • 雙十二大前端工程師讀書清單

    摘要:本文最早為雙十一而作,原標題雙大前端工程師讀書清單,以付費的形式發布在上。發布完本次預告后,捕捉到了一個友善的吐槽讀書清單也要收費。這本書便從的異步編程講起,幫助我們設計快速響應的網絡應用,而非簡單的頁面。 本文最早為雙十一而作,原標題雙 11 大前端工程師讀書清單,以付費的形式發布在 GitChat 上。發布之后在讀者圈群聊中和讀者進行了深入的交流,現免費分享到這里,不足之處歡迎指教...

    happen 評論0 收藏0
  • 雙十二大前端工程師讀書清單

    摘要:本文最早為雙十一而作,原標題雙大前端工程師讀書清單,以付費的形式發布在上。發布完本次預告后,捕捉到了一個友善的吐槽讀書清單也要收費。這本書便從的異步編程講起,幫助我們設計快速響應的網絡應用,而非簡單的頁面。 本文最早為雙十一而作,原標題雙 11 大前端工程師讀書清單,以付費的形式發布在 GitChat 上。發布之后在讀者圈群聊中和讀者進行了深入的交流,現免費分享到這里,不足之處歡迎指教...

    余學文 評論0 收藏0
  • 雙十二大前端工程師讀書清單

    摘要:本文最早為雙十一而作,原標題雙大前端工程師讀書清單,以付費的形式發布在上。發布完本次預告后,捕捉到了一個友善的吐槽讀書清單也要收費。這本書便從的異步編程講起,幫助我們設計快速響應的網絡應用,而非簡單的頁面。 本文最早為雙十一而作,原標題雙 11 大前端工程師讀書清單,以付費的形式發布在 GitChat 上。發布之后在讀者圈群聊中和讀者進行了深入的交流,現免費分享到這里,不足之處歡迎指教...

    Jochen 評論0 收藏0

發表評論

0條評論

hlcc

|高級講師

TA的文章

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