閑談異步調(diào)用“扁平”化

2018-06-09 10:43 更新

哦,代碼……就把它們當(dāng)成插圖吧

隨著 CPU 從單核變多核,軟件從注重功能到注重體驗(yàn),Web 從頁(yè)面跳轉(zhuǎn)方式到 Web2.0 的無(wú)刷新加載(AJAX),程序員越來(lái)越多的接觸多線程和異步。而 Android 的主線程中不允許操作網(wǎng)絡(luò),更是將程序員們推向了異步的深淵。異步深淵產(chǎn)生的主要原因是回調(diào),這在 nodejs 里尤其嚴(yán)重。

// [node.js]

doFirstThing(function(err, data) {
    doSecondThing(data, function(err, data) {
        doThirdThing(data, function(err, data) {
            // ... fall down
        })
    });
});

為了逃離回調(diào)的深淵,大家開(kāi)始想各種辦法來(lái)把回調(diào)扁平化。

在 JavaScript 中,Promise(指該類(lèi)方法而非某個(gè)庫(kù))成為眾多解決方案的佼佼者,并成功被 ES6 采納。

// [node.js]

doFirstThing()
    .then(doSecondThing)
    .then(doThirdThing)
    // .then(...) - continuous
    .catch(function() {
        // something wrong
    });

而 C# 則通過(guò) Task 和 ThreadPool 讓異步編程變得容易。前面談?wù)摰?C# 并行計(jì)算(Parallel 和 ParallelQuery) 就是基于 Task 的。不過(guò)使用 Task.ContinueWith()Parallel.ForEach() 的時(shí)候,就會(huì)覺(jué)得:這不就是 Promise 嗎?

// [csharp]

Task.Run(() => {
    // do first thing and return a data
    return "first";
}).ContinueWith(task => {
    // do second thing and return a data
    return new []{ task.Result, "second" };
}).ContinueWith(task => {
    // do final thing
    foreach (string s in task.Result) { Console.WriteLine(s); }
}).Wait();

之后 C#5.0 又推出 async/await 方案,將異步代碼寫(xiě)得像同步代碼一樣,由編譯器來(lái)將 async/await 代碼封裝成異步 Task 方式,而不是由人工處理,這大大降低了寫(xiě)異步程序的門(mén)檻。

// [csharp]

private async static Task Process() {
    string first = await Task.Run(() => "first");
    string[] all = await Task.Run(() => {
        return new [] { first, "second" };
    });
    
    await(Task.Run(() => {
        foreach (string s in all) { Console.WriteLine(s); }
    }));
}

JavaScript 程序員們一邊在等著 ES7 的 async/await 特性,一邊也沒(méi)閑著,在 ES6 generator/yield 特性基礎(chǔ)上開(kāi)發(fā)了個(gè) co 出來(lái),提供了類(lèi)似于 async/await 特性的異步編程方式。

// [node.js]

var co = require("co");

co(function*() {
    var first = yield new Promise(function(resolve) {
        setTimeout(function() {
            resolve("first");
        }, 100);
    });

    var all = yield new Promise(function(resolve) {
        setTimeout(function() {
            resolve([first, "second"])
        }, 200);
    })

    console.log(all);
});

// [ 'first', 'second' ]

不過(guò) co 程序?qū)懫饋?lái)又要寫(xiě) Generator,又要用 yeild,還常常要封裝 Promise,代碼仍然不夠簡(jiǎn)潔。所以繼續(xù)期待 ES7 的 async/await 特性。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)