async,await执行流看不懂?看完这篇以后再也不会了

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介:

async,await执行流看不懂?看完这篇以后再也不会了
昨天有朋友在公众号发消息说看不懂await,async执行流,其实看不懂太正常了,因为你没经过社会的毒打,没吃过牢饭就不知道自由有多重要,没生过病就不知道健康有多重要,没用过ContinueWith就不知道await,async有多重要,下面我举两个案例佐证一下?

一:案例一 【嵌套下的异步】
写了这么多年的程序,相信大家都知道连接数据库少不了这几个对象,DbConnection,DbCommand,DbDataReader等等。。先来看看ContinueWith在连接数据库时嵌套过深的尴尬。

  1. NetFramework 4.0之前的写法
    这个时期的代码没有什么好说的,都是程式代码,一撸到底,简洁明了。
    public static int SyncGetCount()
    {
        using (var connection = new MySqlConnection("server=xxx.xxx.xxx.xxx;userid=xxx;password=xxx;database=xxx;charset=utf8;port=3306;"))
        {
            connection.Open();
            using (var command = connection.CreateCommand())
            {
                command.CommandText = "select count(1) from messages";

                var count = command.ExecuteScalar();

                Console.WriteLine($"记录条数:{count}");

                return Convert.ToInt32(count);
            }
        }
    }

-------- output -------------

记录条数:75896

  1. NetFramework 4.0下ContinueWith的写法
    当年异步和并发编程概念特别火,火热度参考现在的直播带货,这个时期的C#率先使用新的Task一网兜,在数据库操作的几大类中开始有了Async结尾的方法,如OpenAsync,ExecuteScalarAsync,ReadAsync 等等,但遗憾的是那时写异步,只能像下面这样写。
    public static Task<object> ContinueWithGetCount()
    {
        var connection = new MySqlConnection("server=xxx.xxx.xxx.xxx;userid=xxx;password=xxx;database=xxx;charset=utf8;port=3306;");

        var task = connection.OpenAsync().ContinueWith(t1 =>
         {
             var command = connection.CreateCommand();

             command.CommandText = "select count(1) from messages";

             return command.ExecuteScalarAsync().ContinueWith(t2 =>
                                                              {
                                                                  command.Dispose();
                                                                  connection.Dispose();

                                                                  Console.WriteLine($"记录条数:{t2.Result}");

                                                                  return t2.Result;
                                                              });
         }).Unwrap();
        return task;
    }

-------- output -------------

记录条数:75896

相比同步代码,这异步代码写的是不是很憋屈,为了应对渐进式的Async方法,我不得不进行ContinueWith的深层嵌套,如果Async更多,那对可读性将是毁灭性的打击,这就是所谓的回调地狱。

  1. NetFramework 4.5 下 await,async的写法
    写到这里让我想起了邢老大的那本自传书《左手梦想,右手疗伤》,这苦这心酸只有真正经历过的人才会懂,没有人能够随随便便成功,接下来大家的期望就是如何做到有同步式的代码又有异步功效,鱼和熊掌我都要,当然是可以的,看看如何用await,async进行改造。
    public static async Task<int> AsyncGetCount()
    {
        using (var connection = new MySqlConnection("server=xxx.xxx.xxx.xxx;userid=xxx;password=xxx;database=xxx;charset=utf8;port=3306;"))
        {
            await connection.OpenAsync();
            using (var command = connection.CreateCommand())
            {
                command.CommandText = "select count(1) from messages";

                var count = await command.ExecuteScalarAsync();

                Console.WriteLine($"记录条数:{count}");

                return Convert.ToInt32(count);
            }
        }
    }

-------- output -------------

记录条数:75896

上面这代码太简洁了,眼花的朋友还以为是同步代码呢? 改造的地方也仅仅是方法签名处加上一个async,异步方法前加上await,相当于痛苦版的ContinueWith。

二:案例二 【循环下的异步】
上一个案例只是使用ExecuteScalarAsync从数据库中读取一个值来得到表中的记录数,在业务开发中更多的是使用ExecuteReader从数据库中获取批量记录,这个就涉及到了如何在循环中使用异步,想想就太苦难了(┬_┬)。

  1. NetFramework 4.0之前的写法
    这里我从messages表中读取5条记录,然后输出到控制台,详细代码如下:
    public static List<string> SyncGetMessageList()
    {
        var messageList = new List<string>();
        using (var connection = new MySqlConnection("server=xxx.xxx.xxx.xxx;userid=xxx;password=xxx;database=xxx;charset=utf8;port=3306;"))
        {
            connection.Open();
            using (var command = connection.CreateCommand())
            {
                command.CommandText = "select message from messages limit 5;";
                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        messageList.Add(reader.GetString("message"));
                    }
                }
            }
        }
        messageList.ForEach(Console.WriteLine);
        return messageList;
    }

------------- output ----------------

你需要忘记失去的,感激拥有的,和期待将至的。
以前的找不到了。
对于编译错误,删除Pods文件夹然后重新pod install已经成为经验。次。
Hello,Is there anyone here?
放松心情

  1. NetFramework 4.0下ContinueWith的写法
    要想用ContinueWith完成这功能,最简单有效的办法就是使用递归,用递归的方式把若干个ContinueWith串联起来,而要用递归的话还要单独定义一个方法,写的有点乱,大家将就着看吧。
public class Program
{
    public static void Main(string[] args)
    {
        var task = ContinueWithAsyncGetMessageList();

        task.Result.ForEach(Console.WriteLine);

        Console.Read();
    }

    public static Task<List<string>> ContinueWithAsyncGetMessageList()
    {
        var connection = new MySqlConnection("server=xxx.xxx.xxx.xxx;userid=xxx;password=xxx;database=xxx;charset=utf8;port=3306;");

        var task = connection.OpenAsync().ContinueWith(t1 =>
         {
             var messageList = new List<string>();

             var command = connection.CreateCommand();

             command.CommandText = "select message from messages limit 5;";

             return command.ExecuteReaderAsync().ContinueWith(t2 =>
             {
                 var reader = (MySqlDataReader)t2.Result;
                 return GetMessageList(reader, messageList).ContinueWith(t3 =>
                 {
                     reader.Dispose();
                     command.Dispose();
                     connection.Dispose();
                 });
             }).Unwrap().ContinueWith(t3 => messageList);

         }).Unwrap();

        return task;
    }

    /// <summary>
    /// 采用递归处理循环
    /// </summary>
    /// <param name="reader"></param>
    /// <param name="messageList"></param>
    /// <returns></returns>
    public static Task<List<string>> GetMessageList(MySqlDataReader reader, List<string> messageList)
    {
        var task = reader.ReadAsync().ContinueWith(t =>
          {
              if (t.Result)
              {
                  var massage = reader.GetString("message");
                  messageList.Add(massage);
                  return GetMessageList(reader, messageList);
              }
              else
              {
                  return Task.FromResult(new List<string>());
              }
          }).Unwrap();

        return task;
    }
}

------------ output ----------------
你需要忘记失去的,感激拥有的,和期待将至的。
以前的找不到了。
对于编译错误,删除Pods文件夹然后重新pod install已经成为经验。次。
Hello,Is there anyone here?
放松心情

在递归下探的过程中把messageList集合给填满了,而后将messageList返回给调用端即可,如果没看明白,我画一张图吧!

  1. NetFramework 4.5 下 await,async的写法
    😄,刚刚是不是噩梦般经历,救世主来啦,还是要鱼和熊掌一起兼得。
    public static async Task<List<string>> AsyncGetMessageList()
    {
        var messageList = new List<string>();
        using (var connection = new MySqlConnection("server=xxx.xxx.xxx.xxx;userid=xxx;password=xxx;database=xxx;charset=utf8;port=3306;"))
        {
            await connection.OpenAsync();
            using (var command = connection.CreateCommand())
            {
                command.CommandText = "select message from messages limit 5;";
                using (var reader = await command.ExecuteReaderAsync())
                {
                    while (await reader.ReadAsync())
                    {
                        messageList.Add(reader["message"].ToString());
                    }
                }
            }
        }
        return messageList;
    }

------------ output ----------------
你需要忘记失去的,感激拥有的,和期待将至的。
以前的找不到了。
对于编译错误,删除Pods文件夹然后重新pod install已经成为经验。次。
Hello,Is there anyone here?
放松心情

天底下还有如此简洁的代码就可以实现ContinueWith那种垃圾般代码所实现的功能,我都想仰天长啸,我太难了。

三:总结
还是那句话,你没有被伤过,永远不会体会到那种刻骨铭心的痛。

原文地址https://www.cnblogs.com/huangxincheng/p/12752849.html

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
6月前
|
C#
C#学习系列相关之多线程(四)----async和await的用法
C#学习系列相关之多线程(四)----async和await的用法
|
6月前
|
前端开发 开发者
【面试题】手写async await核心原理,再也不怕面试官问我async await原理
【面试题】手写async await核心原理,再也不怕面试官问我async await原理
172 1
|
6月前
|
前端开发 JavaScript API
一盏茶的功夫帮你彻底搞懂JavaScript异步编程从回调地狱到async/await
在深入讨论 async/await 之前,我们需要了解一下 JavaScript 的单线程和非阻塞的特性。JavaScript 是单线程的,也就是说在任何给定的时间点,只能执行一个操作。然而,对于需要大量时间的操作(例如从服务器获取数据),如果没有适当的管理机制,这种单线程特性可能会导致应用程序的阻塞。为了解决这个问题,JavaScript 引入了回调函数和后来的 Promise,用来管理这些异步操作。
|
6月前
|
前端开发 JavaScript 程序员
(面试题)面试官为啥总是让我们手撕call、apply、bind?
(面试题)面试官为啥总是让我们手撕call、apply、bind?
|
6月前
|
前端开发
【特别甜的语法糖】async 与 await
【特别甜的语法糖】async 与 await
50 0
|
前端开发
浅尝一颗语法糖 async / await
这是 ES7 提供的语法糖,是真的很友好!
71 0
|
前端开发 JavaScript
web前端面试高频考点——JavaScript 篇(二)【JS 异步进阶】Event Loop、then 和 catch、async/await、宏任务微任务、手撕 Promise 源码
web前端面试高频考点——JavaScript 篇(二)【JS 异步进阶】Event Loop、then 和 catch、async/await、宏任务微任务、手撕 Promise 源码
175 0
对于async和await的使用方式、作用效果不怎么理解 ?没关系,初步看这篇就够了
对于async和await的使用方式、作用效果不怎么理解 ?没关系,初步看这篇就够了
|
前端开发 JavaScript
手写async await的 20 行最简实现,阿里面试热门题
如果让你手写async函数的实现,你是不是会觉得很复杂?这篇文章带你用20行搞定它的核心。
|
Serverless C语言 Python
学编程这么久,还傻傻分不清什么是方法(method),什么是函数(function)?
在标准库inspect 中,它提供了两个自省的函数,即 ismethod() 和 isfunction(),可以用来判断什么是方法,什么是函数。
316 0
学编程这么久,还傻傻分不清什么是方法(method),什么是函数(function)?