天天看點

閑談異步調用扁平化

哦,代碼……就把它們當成插圖吧

随着 CPU 從單核變多核,軟體從注重功能到注重體驗,Web 從頁面跳轉方式到 Web2.0 的無重新整理加載(AJAX),程式員越來越多的接觸多線程和異步。而 Android 的主線程中不允許操作網絡,更是将程式員們推向了異步的深淵。異步深淵産生的主要原因是回調,這在 nodejs 裡尤其嚴重。

// [node.js]

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

為了逃離回調的深淵,大家開始想各種辦法來把回調扁平化。

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

// [node.js]

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

而 C# 則通過 Task 和 ThreadPool 讓異步程式設計變得容易。前面談論的 C# 并行計算(Parallel 和 ParallelQuery) 就是基于 Task 的。不過使用

Task.ContinueWith()

Parallel.ForEach()

的時候,就會覺得:這不就是 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

方案,将異步代碼寫得像同步代碼一樣,由編譯器來将

async/await

代碼封裝成異步 Task 方式,而不是由人工處理,這大大降低了寫異步程式的門檻。

// [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); }
    }));
}           
// [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' ]           

繼續閱讀