Osheep

时光不回头,当下最重要。

异步编程的前世今生

异步编程的前世今生

1、为什么需要异步编程

异步编程是相对同步编程来说的,开发项目时,开发者总是希望,程序的执行顺利能按照编程的顺序从上至下执行,这样符合人的思维易于理解,但是现实开发中,一般都事与愿违,相信每个开发者或多或少遇到过,程序执行到某些复杂的、耗时的任务时,往往要开启异步任务去执行,这样就不会阻塞当前任务,让当前任务继续执行,当异步任务执行完后,再将异步任务执行完的结果传给当前任务使用。所以异步编程主要为提高程序整体的执行效率。

2、异步编程模型

以JavaScript语言为例:

2.1、最传统的异步编程模型:异步 + 回调

function sync(callback) {
     你自己的代码;
     async(function() {
          var result = 你自己的代码;
          callback(result);
     });
}

如下是node.js API的示例:

var agent = require('superagent');

agent.get('http://www.bing.com')
     .end(function(err, res) {
          if(err) {
               console.log(err);
          } else {
               console.log('http status: ' + res.status);
               console.log(res.header);
          }
     });

简单这样一层异步回调,代码可读性还好,当遇到多层嵌套回调的场景时,简直可以说是反人类的东西,可读性变得很差,看起来也很难受。

例如下面这段回调嵌套代码:

var fs = require('fs');

fs.readFile('./a.txt', function(err1, data1) {
     fs.readFile('./b.txt', function(err2, data2) {
          fs.writeFile('./ab.txt', data1 + data2, function(err) {
               console.log('read and write done!');
          });
     });
});

看完之后整个人都不好了。

为了缓解这种问题,Promise出现了。

2.2、更友好的异步编程模型:Promise

Promise风格异步函数的基本写法:

如果用setTimeout来模拟要进行的异步操作,以下是让异步函数返回promise的基本写法。调用Promise构造函数,生成一个promise对象,然后return它。把你的代码包裹在匿名函数function(resolve, reject){ … } 里面,作为参数传给Promise构造函数。resolve和reject是promise机制内部已经定义好的函数,传给你用来改变promise对象的状态。在你的异步代码结束的时候调用resolve来表示异步操作成功,并且把结果传给resolve作为参数,这样它可以传给下一个异步操作。

function asyncFunc() {
    var promise = new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log('asyncFn1 is done');
            resolve('asyncFn1 value');
        }, 1000);
    });

   return promise;
}

例如有三个异步函数:

function asyncFunc1() {
    var promise = new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log('asyncFunc1 is done');
            resolve('asyncFunc1 value');
        }, 1000);
    });
    return promise;
}

function asyncFunc2(arg) {
    var promise = new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log('asyncFunc2 is done');
            resolve(arg + ' asyncFunc2 value');
        }, 1000);
    });
    return promise;
}

function asyncFunc3(arg) {
    var promise = new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log('asyncFunc3 is done');
            resolve(arg + ' asyncFunc3 value');
        }, 1000);
    });
    return promise;
}

通过Promise.then()串联异步函数,使得异步函数一个接着一个的顺序执行。

asyncFunc1()
.then(asyncFunc2)
.then(asyncFunc3)
.then(function(arg) {
    console.log(arg);
});

用Promise组织异步函数,完全能取代回调嵌套,关键是代码可读性变得友好。

2.3、异步编程终极模型:async/await

async函数返回的是一个 Promise 对象,然后通过await等待返回值,如果返回值是Promise对象,就会阻塞后面代码的执行,直到等着Promise对象resolve,然后得到resolve的值,才停止阻塞继续执行后面的代码。

前面的例子,如果用asyn/await来实现,如下:

async function doSomething() {
    const temp1 = await asyncFunc1();
    const temp2 = await asyncFunc2(temp1);
    const result = await asyncFunc3(temp2);
    console.log(`result is ${result}`);
}

简直太简洁,代码可读性非常好,关键还非常符合人的思维。

其实关于async/await这种异步编程模型,在一些其他编程语言叫做:协程。
比如:C#,Python等都有async/await的异步编程,此方式最大的优点就是以编写同步代码的方式实现异步编程。

最后还想提一下,Kotlin语言推出的协程,涵盖几乎所有编程语言(C#,Python,Go等语言原生支持协程)协程的实现方式,可以预见未来Java、Android开发的异步编程很美好。

点赞