JavaScript 非同步程式革命-async、await 與 TypeScript 2.1

.NET 4 的 Task 大幅簡化多執行緒程式碼複雜度,而 4.5 推出的 async/await (參考)則讓非同步程式的寫法更精簡。(註:在博客園找到一篇從 Task、ThreadPool 談到 await 的廣泛介紹,可以一讀)

網頁開發老鳥都知道,JavaScript 端的非同步邏輯不比 C# 簡單,複雜起來會讓人寫程式寫到嘔出幾十兩鮮血。於是在前端也看到類似 Thread、Task、async/await 的發展史:最早只能靠 Callback、setTimeout 苦手硬刻,ES6 有了 Promise, 到了 ES7,async/await 也出現了。撤底搞懂 ES6 Promise、Generator,ES7 async/await 是很有成就感的事(如果你想試試的話: 1 2),不過如果你跟我一樣,要寫前端但不想走在最前端,讓 TypeScript 負責封裝背後的複雜性,輕鬆下指令也是很好的選擇。

TypeScript 早在 1.7 版就支援 async 與 await,但有個大罩門,編譯出來的 JavaScript 只能在 ES2015/ES6 目標平台執行(參考:ES 規格檢查表),連 IE11 都不支援,對必須考量 IE 相容的開發者來說,英雄無用武之地,差不多是這種感覺:

前陣子推出的 TypeScript 2.1 有個天大好消息,async / await 向下相容 ES5 目標平台,IE9 嘛A通,都到了這份兒上,還有不學不用的道理?

先來看個範例:

<%@ Page Language="C#" %>
 
<script runat="server">
    void Page_Load(object sender, EventArgs e)
    {
        var m = Request["m"];
        if (m == "guid")
        {
            System.Threading.Thread.Sleep(2000);
            Response.Write(Guid.NewGuid().ToString());
            Response.End();
        }
        else if (m == "time")
        {
            System.Threading.Thread.Sleep(1000);
            Response.Write(DateTime.Now.ToString("HH:mm:ss"));
            Response.End();
        }
    }
</script>
 
<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="utf-8" />
</head>
<body>
    <button>Call AJAX</button>
    <div class="msg"></div>
    <script src="Scripts/jquery-3.1.1.js"></script>
    <script src="Scripts/promise.js"></script>
    <script src="Scripts/async.js"></script>
</body>
</html>

網頁流程為按下按鈕後先呼叫 ?m=guid 由 Server 端取得 Guid,再呼叫 ?m=time 取回時間,將結果顯示在<div>上。若以 jQuery.get 實作,程式如下:

    var div = $(".msg");
    var button = $("button");
    button.click(() => {
        button.prop("disabled", true);
        $.get("/Lab.aspx?m=guid").done((guid) => {
            div.text(guid);
            $.get("/Lab.aspx?m=time").done((time) => {
                div.text(div.text() + " " + time);
                button.prop("disabled", false);
            });
        });
    });

執行結果:

接著我們用 async/await 來改寫一番:

    var div = $(".msg");
    var button = $("button");
 
    async function doAjaxJob() {
        button.prop("disabled", true);
        await $.get("/Lab.aspx?m=guid").then(guid => div.text(guid));
        await $.get("/Lab.aspx?m=time").then((time) => {
            div.text(div.text() + " " + time);
            button.prop("disabled", false);
        });
    }
 
    button.click(doAjaxJob);

跟 C# 做法類似,function 加上 async 後,在其中便可使用 await 等待非同步程式結束再往下執行,await 函式傳回的必須是 Promise,而先前提到 jQuery 3 特意調整符合 Promise/A+ 規範的優勢也在此展現,$.get() 傳回值為 Promise 可直接搭配 await 使用,寫法簡潔易讀許多。

async 寫法在 TypeScript 2.1 以前有目標平台限制,低於  ES6(ES2015)會出現錯誤訊息。

安裝TypeScript for VS2015 2.1+版可以解除限制,編譯出相容 ES5 的程式碼。但由於 ES5 欠缺 Promise 定義,還需要使用 NuGet 安裝 es-promise 或其他包含 Promise 的 TypeScript 定義檔,才能順利編譯。

再稍稍走深一點,切換 ES 版本來觀察 TypeScript 如何在 ES5 及 ES6 實現 async / await:

TypeScript 在 ES5 實現 async、await 的祕密如下:

很可怕,不要問。不過也不需要懂(有興趣搞懂可以看這篇),有了 TypeScript 2.1, 我們放心寫 async / await 就好,就算要相容 IE9-11 也不怕,再加上先前介紹過的 Template String,教人怎能不愛它?

最後用一個美妙的 async/await 範例收尾,大家知道要怎麼在 JavaScript 實現 Thread.Delay(…) 嗎?Yes,setTimeout(),如果要做到一秒後印1s,再兩秒後印3s,再3秒後印6s,程式大約會像這樣:

    var div = $(".msg");
    var button = $("button");
 
    function doAjaxJob() {
        button.prop("disabled", true);
        setTimeout(() => {
            div.text("1s");
            setTimeout(() => {
                div.text("3s");
                setTimeout(() => {
                    div.text("6s");
                    button.prop("disabled", false);
                }, 3000);
            }, 2000);
        }, 1000);
    }
 
    button.click(doAjaxJob);

讓我們用 await 改寫看看:

    var div = $(".msg");
    var button = $("button");
 
    async function delay(duration: number) {
        return new Promise((resolve, reject) => {
            setTimeout(resolve, duration);
        });
    }
 
    async function doAjaxJob() {
        button.prop("disabled", true);
        await delay(1000);
        div.text("1s");
        await delay(2000);
        div.text("3s");
        await delay(3000);
        div.text("6s");
    }
 
    button.click(doAjaxJob);

看到 JavaScript 可以 Thread.Sleep,程式還這麼清爽,我感動到都快哭了~ 快升級 TypeScript 2.1 感受一下吧!

歡迎推文分享:
Published 23 December 2016 06:33 AM 由 Jeffrey
Filed under:
Views: 9,458



意見

沒有意見

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<December 2016>
SunMonTueWedThuFriSat
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567
 
RSS
創用 CC 授權條款
【廣告】
twMVC

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication