後端程式設計師的 Js 之旅 : 回調地獄終結者

2022-01-08 前端大全

(點擊上方公眾號,可快速關注)

作者:李少鵬(@BenjaLi) 

連結:http://lishaopeng.com/2016/03/06/js-cbhell/


回調地獄

對 JavaScript 程式設計師來說,處理回調是家常,但是處理層次過深的回調就沒有那麼美好了,下面的示例代碼片段用了三層回調,再補腦一下更多層的場景,簡直是酸爽,這就是傳說中的回調地獄。

getDirectories(function(dirs) {

    getFiles(dirs[0], function(files) {

        getContent(files[0], function(file, content) {

            console.log('filename:', file);

            console.log(content);

        });

    });

});

 

function getDirectories(callback) {

  setTimeout(function() {

    callback(['/home/ben']);

  }, 1000);

}

 

function getFiles(dir, callback) {

    setTimeout(function() {

        callback([dir + '/test1.txt', dir + '/test2.txt']);

    }, 1000)

}

 

function getContent(file, callback) {

    setTimeout(function() {

        callback(file, 'content');

    }, 1000)

}

解決方案

生態圈中有很多異步解決方案可以處理回調地獄的問題,比如 bluebird、Q 等,本文重點介紹 ECMAScript 6/7 規範中對異步編程的支持。

ES6 Promise

Promise 是一種異步編程的解決方案,是解決回調地獄問題的利器。

Promise 在 JavaScript 生態圈被主流接受是在 2007 年 Dojo 框架增加了 dojo.Deferred 的功能。隨著 dojo.Deferred 的流行,在 2009 年 Kris Zyp 提出了 CommonJS Promises/A 規範。隨後生態圈中出現了大量 Promise 實現包括 Q.js、FuturesJS 等。當然 Promise 之所有這麼流行很大程度上是由於 jQuery 的存在,只是 jQuery 並不完全遵守 CommonJS Promises/A 規範。隨後正如大家看到的,ES 6 規範包含了 Promise。

MDN 中對 Promise 是這樣描述的:

Promise 對象是一個返回值的代理,這個返回值在promise對象創建時未必已知。它允許你為異步操作的成功或失敗指定處理方法。 這使得異步方法可以像同步方法那樣返回值:異步方法會返回一個包含了原返回值的

以下的代碼是「回調地獄」一節中的示例通過 Promise 實現,看上去代碼也不是很簡潔,但是比起傳統的層級回調有明顯改善,代碼可維護性和可讀性更強。

getDirectories().then(function(dirs) {

    return getFiles(dirs[0]);

}).then(function(files) {

    return getContent(files[0]);

}).then(function(val) {

    console.log('filename:', val.file);

    console.log(val.content);

});

 

function getDirectories() {

    return new Promise(function (resolve, reject) {

        setTimeout(function() {

        resolve(['/home/ben']);

      }, 1000);

    });

}

 

function getFiles(dir) {

    return new Promise(function (resolve, reject) {

        setTimeout(function() {

            resolve([dir + '/test1.txt', dir + '/test2.txt']);

        }, 1000);

    });

}

 

function getContent(file) {

    return new Promise(function (resolve, reject) {

        setTimeout(function() {

            resolve({file: file, content: 'content'});

        }, 1000);

    });

}

ES6 Generator

Promise 的實現方式還不夠簡潔,我們還需要更好的選擇,co 就是選擇之一。co 是基於 Generator(生成器)的異步流控制器,了解 co 之前首先需要理解 Generator。熟悉 C# 的同學應該都有了解,C# 2.0 的版本就引入了 yield 關鍵字,用於迭代生成器。ES 6 Generator 跟 C# 相似,也使用了 yield 語法糖,內部實現了狀態機。具體用法可以參考 MDN 的文檔 function* 一節,原理可以參考AlloyTeam 團隊 Blog 深入理解 Generator。使用 co 巧妙結合 ES6 Generator 和 ES6 Promise 讓異步調用更加和諧。

co(function* (){

    var dirs = yield getDirectories();

    var files = yield getFiles(dirs[0]);

    var contentVal = yield getContent(files[0]);

    console.log('filename:', contentVal.file);

    console.log(contentVal.content);

});

co 非常巧妙,其核心代碼可以簡化如下的示例,大體思路是採用遞歸遍歷生成器直到狀態完成,當然 co 做的跟多。

runGenerator();

 

function* run(){

    var dirs = yield getDirectories();

    var files = yield getFiles(dirs[0]);

    var contentVal = yield getContent(files[0]);

    console.log('filename:', contentVal.file);

    console.log(contentVal.content);

}

 

function runGenerator(){

    var gen = run();

 

    function go(result){

        if(result.done) return;

        result.value.then(function(r){

            go(gen.next(r));

        });

    }

 

    go(gen.next());

}

ES7 Async/Await

ES6 Generator 確實很好,只可惜需要第三方庫的支持。好消息是 ES 7 會引入 Async/Await 關鍵字完美解決異步調用的問題。好吧,.net 又領先了一步,.net framework 4.5 已經率先支持了。

今後的代碼寫起來是這樣:

run();

async function run() {

    var dirs = await getDirectories();

    var files = await getFiles(dirs[0]);

    var contentVal = await getContent(files[0]);

    console.log('filename:', contentVal.file);

    console.log(contentVal.content);

}

結論

從經典的回調的異步編程方式,到 ES6 Promise 規範對異步編程的改善,再到 co 結合 ES Generator 優雅處理,最後 ES7 async/await 完美收官,可以讓我們了解為什麼 ECMAScript 會出現這些特性以及解決了什麼問題,更加清晰地看到 JavaScript 異步編程發展的脈絡。

參考:

【今日微信公號推薦↓】

更多推薦請看值得關注的技術和設計公眾號

其中推薦了包括技術設計極客 和 IT相親相關的熱門公眾號。技術涵蓋:Python、Web前端、Java、安卓、iOS、PHP、C/C++、.NET、Linux、資料庫、運維、大數據、算法、IT職場等。點擊《值得關注的技術和設計公眾號》,發現精彩!


點擊閱讀原文,了解野狗

相關焦點

  • 關於後端程式設計師寫前端用什麼框架更好?
    不過歸根結底是一套ui皮膚+少量js組成的框架,屬於封裝度偏低的框架。經典頁面大概是這樣:如果說之前的八級至少前端和後端還是相對分離的,後臺程序在java中寫,前臺程序在html或者js中寫;那麼這一級別的框架簡直會顛覆你的認知!只需在後臺使用java寫好了類和對象,並設置好相關的屬性,網頁的元素是自動通過後臺對象生成的。這當然有好處,媽媽再也不用擔心我不會寫js和css了。然而就學一下html/css/js真的那麼難嗎?
  • vue3 專用 indexedDB 封裝庫,基於Promise告別回調地獄
    做一個 help,封裝初始化的代碼前端資料庫和後端資料庫對比一下,就會發現一個很明顯的區別,後端資料庫是先配置好資料庫,建立需要的表,然後添加初始數據,最後才開始運行項目。在項目裡面不用考慮資料庫是否已經建立好了,直接用就行。
  • PHP對戰Node.js:我們曾相愛,想到就心酸
    單憑一種語言,我們已經能夠構建起Node.js以及其它各類運行在客戶機上的框架。「JavaScript無處不在」甚至成為一部分開發人員的工作信仰。當然,這個故事的結局還沒有敲定。對於每一位到處宣揚Node.js先進性以及JavaScript便捷性的開發人員來說,總有跟自己作對的傢伙存在——他們更傾向於PHP深邃的代碼基礎以及長期穩定的實際表現。
  • Node.js對Java:一場史詩級的爭奪開發者注意力的對決
    同時,之前被程式設計師們錯認為是 Java「雙胞胎」的 JavaScript,如今也能獨擋一面了。在 HTML 和 web 推出 Borg 的幾年間,JavaScript 一直緊隨其後,AJAX 的出現使得 JavaScript 突然之間擁有了力量,打破了之前的局面。隨後 Node.js 誕生了,大大提高了開發速度。
  • CF-終結者2:地獄終結者與歷代終結者盤點誰更勝一籌
    穿越火線終結者2新版本更新後,大家肯定都玩過這個版本的主要更新內容,終結者2生化模式了,這次更新出現了新的終結者,地獄終結者,擁有強力的遠程輸出能力並擁有強力的遠程技能(單手螺旋丸)。其實在這代地獄終結者之前,一共還有五個終結者,老玩家都知道,不過有入坑晚的玩家和萌新未必見過,下面就讓我們一起來看看吧。最初代的終結者,也是最經典的終結者,全身散發著離子電荷飄散在半空中,屬性非常出色,顏值也很高,技能來說是個防禦類的技能,可以開啟防護罩抵禦傷害,攻擊時發射電漿溶解對手,是非常難纏的終結者,也是認可度非常高的一個boss級怪物。
  • Ryan Dahl 訪談: Node.js/Deno 的創始人
    他們對保守派程式設計師足夠好嗎?對於某些領域,會有更多的Python庫支持,尤其是在科學計算中,Python可能是合適的,主要是看新程式設計師想要做什麼。但是,總的來說,我認為JavaScript是一種更好的入門語言。
  • D3.js、echar.js 前端必備大數據技能
    現在又是大前端時代,前端不但要求基本的傳統前端技能,也要會後端語言開發,前後配合思想,更是在大數據潮流下,顯得至關重要,因為人人都講究大數據,可你只有枯燥的海量的數據,展示不出來,沒有可視化、可操作的入口,那這些數據也只是一堆二進位而已。囉嗦這麼多,問題來了,我們傲嬌的web前端究竟如何在大數據裡分一杯羹、出一份力、如何在大數據公司,靠技能謀得重要職位呢?
  • CF-終結者2:地獄終結者進攻點位分析與進攻選擇
    當變身超能終結者後就可以選擇在牆後躲起來,地獄終結者一定要躲起來搓球,當面搓球一是容易被發現提前躲避,二是容易被集火帶走,地獄終結者沒有防禦技能,搓求的時候是一個妥妥的肉靶子。當火球搓到差不多最大得時候,直線推出,火球會沿著長廊滾動,傭兵不躲基本是必死的,所以只能選擇放棄這個戰略地點,一旦傭兵散開,放棄了防守點位,就很容易被擊潰。
  • 新年煙花/表白/拜年/情人節/ html+css+js 放一場浪漫煙花秀(含音樂/定製相片)程式設計師表白必備煙花
    新年煙花❤表白/拜年/情人節❤ html+css+js 放一場浪漫煙花秀(含音樂/定製❤相片)程式設計師表白必備煙花/除夕夜/春節/七夕情人節/ 快到了
  • 如何開發 Node.js Native Add-on?
    Node.js 是一個 JavaScript 單線程模型的實現,一個 Node.js 環境只會有一個主線程可以訪問 JavaScript 值。因此,在主線程執行重 CPU 的任務就會導致 JavaScript 程序被阻塞,導致事件與回調都堆積在事件隊列中。
  • 【程式設計師的復仇紀】:11 行代碼讓整個網際網路界都炸開了鍋!
    >客戶動不動就對程式設計師說這個很容易實現呀,你怎麼還沒做出來?  但是,小千編要警告你們,沒事別惹程式設計師!  要理解NPM,首先就要知道Node.js。Node.js是基於Javascript語言的後端編程框架,開發網站用的語言,可以理解為PHP的替代品。Node.js最明顯的特徵在於,除了使用Javascript語言,同時還擁有許許多多公用的模塊,都由來自世界各地的開發者貢獻,這些模塊使用NPM管理,NPM就有點像蘋果的App商店,只不過這些模塊都是免費的。
  • Vue.js開發的4個基本ES2015特性 - 智能甄選
    考慮需要回調函數的JavaScript數組方法。Array.filter例如,允許您返回一個新的數組,只包含那些與回調函數定義的過濾器相匹配的項目。Vue.js的一個重要功能是,您可以輕鬆訪問this.vuePropertyVue配置對象上下文中的數據屬性,計算屬性和方法。但是,如果您使用常規函數進行回調,它將為其自己的值綁定this。
  • 高清完整:React.js框架從入門到精通【百度雲好課分享】
    高清完整:React.js框架從入門到精通【百度雲好課分享】不用懷疑!
  • 揭秘地獄終結者不為人知的來歷
    前幾天,我們聊完了鋼鐵終結者,今天,我們就來開始聊穿越火線地獄終結者的劇情了,開始之前,我要跟大家闢個謠啊,因為有些不懂劇情的哥們在那亂說,說什麼藍色終結者是自己冥想出來的
  • 成為自信的node.js開發者(一)
    先來看一張圖表:最上面是我們編寫的node.js的代碼,當我們執行node index.js的命令時,我們是觸發了一個node的程序,和其他的javascript的項目,比如說前端的h5項目一樣,該node程序需要有其他的依賴,其中最主要的兩個依賴是 v8 和 libuv。
  • Underscore.js 入門教程
    (點擊上方公眾號,可快速關注)英文:Martín Martínez譯文:伯樂在線專欄作者 - 吳鵬煜連結:http://web.jobbole.com/87537/Underscore.js你可以從,比如你所喜愛的 CDN 處獲取:<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>一路走來如果發現自己需要幫助,或者你想要了解更多,不要忘了還有內容更廣泛的 Underscore官方文檔。
  • node.js「公眾檔所」項目解析
    這個東西是一個老物了,在我剛接觸 Expressjs 的時候寫的。當時還隨便搞了一下 backbone.js,但是沒有深入,勿笑。關於深入構架 Expressjs 方面也沒做,只是粗粗寫了下最基礎的路由,所以整個文件結構也不是很規範。但是應該能比較適合剛學 Node.js 以及剛接觸 Expressjs 的人吧。
  • Node.js 4.0.0 正式發布了
    不用驚訝,就是4.0.0,這是 Node.js 項目 和 io.js 項目複合後的首個穩定版本。同時 Node 4.0 將引入 LTS 長期支持計劃的發行周期,首個LTS版本將於10月份發布,同時 Node 以後將每 6 個月發布一個主要的穩定版本,4月份一個,10月份一個(類似 Ubuntu)。
  • 程式設計師需知的 59 個網站
    每天更新乾貨,點藍色字關注「老韓校長」眾所周知,程式設計師是一個需要不斷學習的職業,而幸運的是,在這個網際網路時代,知識就在那裡,等著我們去獲取。推薦指數:網站封面5、Vue.js地 址:https://cn.vuejs.org/簡 介:國內最流行的JS框架,Vue.js的文檔是中文的。
  • 你最喜歡的後端開發框架是什麼?
    新的後端開發或為您的下一個項目尋找新的框架?請閱讀三種流行框架的優缺點。