闲谈异步调用扁平化

哦,代码……就把它们当成插图吧javascript

随着 CPU 从单核变多核,软件从注重功能到注重体验,Web 从页面跳转方式到 Web2.0 的无刷新加载(AJAX),程序员愈来愈多的接触多线程和异步。而 Android 的主线程中不容许操做网络,更是将程序员们推向了异步的深渊。异步深渊产生的主要缘由是回调,这在 nodejs 里尤为严重。java

// [node.js]

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

为了逃离回调的深渊,你们开始想各类办法来把回调扁平化。node

在 JavaScript 中,Promise(指该类方法而非某个库)成为众多解决方案的佼佼者,并成功被 ES6 采纳。git

// [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 方式,而不是由人工处理,这大大下降了写异步程序的门槛。github

// [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 特性,一边也没闲着,在 ES6 generator/yield 特性基础上开发了个 co 出来,提供了相似于 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' ]

不过 co 程序写起来又要写 Generator,又要用 yeild,还经常要封装 Promise,代码仍然不够简洁。目前 async/await 已经在 TypeScript、Babel、Node 7.6+ 等环境中获得支持,使用 JavaScript 的 async/await 不只能大大简化代码,还能下降逻辑思路的复杂度。segmentfault

2018.1.18 更新,由于 async/await 早已进入实践阶段网络

相关文章
相关标签/搜索