1. 程式人生 > >改進 ThinkJS 的非同步程式設計方式

改進 ThinkJS 的非同步程式設計方式

提醒:本文最後更新於 1337 天前,文中所描述的資訊可能已發生改變,請謹慎使用。

ThinkJS 是奇舞團開源的一款 Node MVC 框架,主要由 welefen 開發。簡單介紹一下:

ThinkJS 是一個快速、簡單的基於 MVC 和麵向物件的輕量級 Node.js 開發框架,遵循 MIT 協議釋出。秉承簡潔易用的設計原則,在保持出色的效能和至簡的程式碼同時,注重開發體驗和易用性,為 WEB 應用開發提供強有力的支援。

ThinkJS 借鑑了很多 ThinkPHP 的特性,同時結合 Node.js 的特性,使用了 ES6 Promise,讓非同步程式設計更加簡單、方便。via

我是 ThinkJS 的第一批使用者,大約在 2014 年初,我把我的部落格程式用 ThinkJS 重新實現了一遍,前後花了不到一週。之後的這一年多,我用 ThinkJS 寫過大大小小很多個系統和工具,越用越覺得好用,現在已經完全離不開了。

今天這篇部落格我準備聊聊 ThinkJS 中的非同步程式設計方式以及我採用的方案。

前面介紹中提到過,ThinkJS 是基於 Promise 實現的非同步程式設計。我之前寫的那篇「非同步程式設計:When.js 快速上手」已經比較詳細的介紹了 Promise 相關知識。直接看一段摘自於我部落格程式的程式碼:

indexAction : function() {
    var instance = this;
    return D('Post')
        .getPostList(this.get('pn'), 10)
        .then(function(data) {
            data.pagerPath = getPagerPath(instance.http);
            data.currentPage = 'blog-home'
; instance.assign(data); instance.display('Blog/theme/' + theme + '/post_list.html'); }); }

這段程式碼中 getPostList 是一個查詢資料庫的非同步操作,它的返回值在 then 中才可以拿到。如果 then 中還有其他非同步操作,最後還是會導致巢狀很深。這段程式碼還會遇到經典的 this 指標問題,需要賦值儲存或者用 bind 解決。這段程式碼能不能實現得更優雅一點呢?我們先來看另一段:

async function myFunction
()
{ let result = await somethingThatReturnsAPromise(); console.log(result); // cool, we have a result }

這樣寫非同步邏輯,是不是很贊?既好看又好懂,也沒有 this 指標問題。實際上,這是 ES7 裡的 async function,還得等一陣子才能用。

之前我在介紹 ES6 的生成器函式(generator function)時,曾經舉過一個生成器函式結合 Promise 使用的例子,下面摘錄一段(全文在這裡):

var all = Q.async(function* () {
    var src = yield getData();
    var img = yield getImg(src);
    showImg(img);
});

有沒發現這段程式碼跟上面的 async function 非常像?沒錯!利用生成器函式和 Q 框架,可以方便地把 Promise 巢狀變成平級,這一切現在就能用!

在繼續之前,請通過 node -v 檢查下 Node 版本,推薦升級到最新的 0.12。對於 Ubuntu 系統,可以這樣安裝最新的 0.12:

curl -sL https://deb.nodesource.com/setup_0.12 | sudo bash -
sudo apt-get install -y nodejs

有了最新的 Node,在使用 Node 命令時,還需要帶上 --harmony 引數,例如啟動 ThinkJS:

node --harmony www/index.js

如果用 PM2 啟動程式,也需要帶上這個引數,例如:

pm2 start ~/www/blog/www/index.js -n blog --node-args="--harmony"

安裝並引入 Q 模組之後,就可以開始改造本文第一段程式碼了:

indexAction : Q.async(function* () {
    var data = yield D('Post').getPostList(this.get('pn'), 10);

    data.pagerPath = getPagerPath(this.http);
    data.currentPage = 'blog-home';

    this.assign(data);
    this.display('Blog/theme/' + theme + '/post_list.html');
})

這下,程式碼是不是更清晰明瞭。改動不大,但效果很明顯,上面提出的問題都解決了。

這裡有個小點需要注意下:如果你使用了 ThinkJS 的 Action 引數自動繫結功能,例如這樣:

tagAction : function(tag) {
    return D('Post')
        .getPostListByTag(tag, this.get('pn'), 10)
        .then(function(data) {
            ...
        });
}

改造之後,需要通過 this.get('tag') 獲取引數值:

tagAction : Q.async(function* () {
    var tag = this.get('tag');
    var data = yield D('Post').getPostListByTag(tag, this.get('pn'), 10);
    ...
})

因為 ThinkJS 的引數自動繫結依賴 function.toString,應該沒匹配生成器函式格式。當然下面這樣寫也可以,只是更復雜:

tagAction : function (tag) {
    return Q.spawn(function* (){
        var data = yield D('Post').getPostListByTag(tag, this.get('pn'), 10);
        ...
    }.bind(this));
}

Q.asyncQ.spawn 的文件在這裡可以找到。主要區別是 Q.async 返回等待執行的函式;Q.spawn 會立即執行。

最後放上一大段程式碼結束本文,這是我的部落格詳情頁實現程式碼:

postAction : Q.async(function* () {
    var Post = D('Post');

    var slugOrId = this.get('slugOrId');
    var post = yield Post.getPost(slugOrId);

    //不存在的文章,重定向到首頁
    if(!post) {
        return this.redirect('/');
    } 

    //未公開的文章,非登入使用者訪問時重定向到首頁
    if(post.status != 'publish') {
        var user = yield this.session('user');
        if(isEmpty(user)) {
            return this.redirect('/');
        }
    }

    //如果有 slug,但是用 id 訪問,301 到更友好的 url
    var slug = post.slug;
    if(slug && slug != slugOrId) {
        return this.redirect(format('/post/%s.html', slug), 301);
    }

    var data = {};

    //獲取上一篇、下一篇
    var prevPost = Post.getPrevPost(post.pub_date);
    var nextPost = Post.getNextPost(post.pub_date);
    var morePost = yield Promise.all([prevPost, nextPost]);

    data.prevPost = isEmpty(morePost[0]) ? false : morePost[0];
    data.nextPost = isEmpty(morePost[1]) ? false : morePost[1];

    //獲取 Toc
    var getTocResult = thinkRequire('TocApi')(post.content);
    post.content = getTocResult[0];

    data.toc = getTocResult[1];
    data.tocName = '文章目錄';

    data.currentPage = 'post-' + (post.slug || post.id);
    data.title = post.title + ' | ';

    data.post = post;

    this.assign(data);
    this.display('Blog/theme/' + theme + '/single_post.html');
})

注:後來我又把 Q 換成了 bluebird,bluebird 的 Promise.coroutineQ.async 功能一樣,直接替換即可。

--EOF--

提醒:本文最後更新於 1337 天前,文中所描述的資訊可能已發生改變,請謹慎使用。

相關推薦

改進 ThinkJS非同步程式設計方式

提醒:本文最後更新於 1337 天前,文中所描述的資訊可能已發生改變,請謹慎使用。 ThinkJS 是奇舞團開源的一款 Node MVC 框架,主要由 welefen 開發。簡單介紹一下: ThinkJS 是一個快速、簡單的基於 MVC 和麵向物件的輕量級 Node.js 開發框架,遵循 MIT

初探 CompletableFuture Java8新的非同步程式設計方式

序言        由於專案中想獲取多個非同步執行緒的狀態,並判斷其是否都

JS實現非同步程式設計的幾種方式

轉載出處:http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html Javascript非同步程式設計的4種方法 作者:&nbs

JavaScript是如何工作的:事件迴圈和非同步程式設計的崛起 + 5種使用 async/await 更好地編碼方式

摘要: 深度理解JS事件迴圈!!! 原文:JavaScript是如何工作的:事件迴圈和非同步程式設計的崛起+ 5種使用 async/await 更好地編碼方式! 作者:前端小智 Fundebug經授權轉載,版權歸原作者所有。 此篇是 JavaScript是如何工作的第四篇,其它三篇可以看這

C# 使用委託實現非同步程式設計的四種方式

一、關於委託 1、委託概念:委託是一個類,它定義了方法的型別,使得可以將方法當作另一個方法的引數來進行傳遞      個人角色這個概念有些晦澀難懂,大家可以把委託當成一個方法的指標就好了,可以指向一個方法。或者通過(+=)的方式指向多個。 2、四種宣告方式 1)直接用del

【譯】JavaScript的工作原理:事件迴圈及非同步程式設計的出現和 5 種更好的 async/await 程式設計方式

此篇是JavaScript的工作原理的第四篇,其它三篇可以看這裡: 【譯】JavaScript的工作原理:引擎,執行時和呼叫堆疊的概述 【譯】JavaScript的工作原理:V8引擎內部+關於如何編寫優化程式碼的5個技巧 【譯】JavaScript的工作原理:記憶體管理和4種常見的記憶體洩漏

非同步程式設計方式async/await

一、前言   實際上對async/await並不是很陌生,早在阮大大的ES6教程裡面就接觸到了,但是一直處於理解並不熟練使用的狀態,於是決定重新學習並且總結一下,寫了這篇博文。如果文中有錯誤的地方還請各位批評指正! 二、介紹async/await   1.async/await 是非同步程式碼的新方式   

Scala 非同步程式設計之 Future (二)

上篇部落格講了scala中的Future,實際上java 在jdk1.5後增加了callable,也實現了Future,以《Netty In Action》中的程式碼為例,看一下java中Future的實現: import java.util.concurrent.Callable; im

Scala 非同步程式設計之 Future

同步非同步,阻塞非阻塞,在IO模型中幾個概念組合在一起不是很容易理解,但是隻從程式碼執行的角度看同步非同步是很清晰的: 同步代表這段程式碼中的邏輯必須執行完畢,而非同步代表呼叫馬上返回,但通常情況下是獲取不到需要的值。 同步:val  value={ Thread.sleep(

Dart非同步程式設計之Stream

Dart非同步程式設計包含兩部分:Future和Stream 上篇文章已介紹了Future,此篇文章為大家介紹下另一塊–Stream Dart 非同步事件流 Stream 基本概念 顧名思義,Stream 就是流的意思,表示發出的一系列的非同步資料。可以簡單地認為 Strea

Dart非同步程式設計之Future

Dart非同步程式設計包含兩部分:Future和Stream 本文將詳細介紹Future Dart非同步程式設計-future 非同步程式設計:Futures Dart是一個單執行緒程式語言。如果任何程式碼阻塞執行緒執行都會導致程式卡死。非同步程式設計防止出現阻塞操作。Dar

return函式提前傳參和麵向物件的程式設計方式

function request({method,data}){f.c(data)}   function get(v){ return request({method:'GET', data:v}) } f

java非同步程式設計降低延遲

目錄 java非同步程式設計降低延遲 一、ExecutorService和CompletionService 二、CompletableFuture(重要) 三、stream中的parallel(並行流) 四、實際使用的另外一點總結: java非同步

Vert.x(五): Vert.x-通過非同步方式使用JDBC連線SQL

歡迎關注http://quanke.name/ 交流群:231419585 轉載請註明出處,謝謝 在這篇文章中,我們將會看到怎樣在vert.x應用中使用HSQL,當然也可以使用任意JDBC,以及使用vertx-jdbc-

JavaScript非同步程式設計筆記

非同步事件的工作方式 事件!事件到底是怎麼工作的?JavaScript出現了多久,對JavaScript非同步事件模型就迷惘了多久。迷惘導致bug,bug導致加班,加班導致沒時間撩妹子,這不是js攻城獅想要的生活。 ==為了妹子,一定要理解好JavaScript事件== JavaScript事件的執行

【FastReport教程】介紹C#中的非同步程式設計(下)

【下載FastReport.Netdownload最新版本】 非同步程式設計模型出現在.Net Framework的第一個版本中。APM允許使用兩種方法建立同步方法的非同步版本 - Begin 和End 。 所以,只有兩種方法: public IAsyncResult Begin{MethodName}(

從CompletableFuture到非同步程式設計設計

從CompletableFuture到非同步程式設計設計,筆者就分為2部分來分享CompletableFuture非同步程式設計設計,前半部分總結下CompletableFuture使用實踐,後半部分分享下CompletableFuture實現原理和非同步程式設計設計機制。 (ps:本

非同步程式設計Async/Await中的最佳做法

近日來,湧現了許多關於 Microsoft .NET Framework 4.5 中新增了對 async 和 await 支援的資訊。 本文旨在作為學習非同步程式設計的“第二步”;我假設您已閱讀過有關這一方面的至少一篇介紹性文章。 本文不提供任何新內容,Stack Overflo

關於程式設計方式

隨著計算機軟體技術的發展,新平臺和方法越來越多,庫函式、開發語言也不斷增多,僅靠一個人的能力是無法勝任複雜系統的開發的。 現在的程式設計方式主要是人與人之間的分工合作協調。 未來的程式設計也許會用機器,機器可以不斷的學習,如果機器智慧化水平越來越高,或許,有一天它就能編寫出優秀的程式碼。畢竟,人

非同步程式設計的 async/await

async/await 和 Generators + co 的寫法非常的相似,只是把用於宣告 Generator 函式的 * 關鍵字替換成了 async 並寫在了 function 關鍵字的前面,把 yield 關鍵字替換成了 await;另外,async 函式是基於 Promise 的