异步编程的前世今生

作者: 萧雾宇 | 来源:发表于2017-09-09 19:02 被阅读324次

    异步编程的前世今生

    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开发的异步编程很美好。

    相关文章

      网友评论

        本文标题:异步编程的前世今生

        本文链接:https://www.haomeiwen.com/subject/utjajxtx.html