前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从Swift 5.5引进async/await聊起,异步编程会成为未来么?

从Swift 5.5引进async/await聊起,异步编程会成为未来么?

作者头像
御剑
发布2021-10-19 14:06:26
1.4K0
发布2021-10-19 14:06:26
举报
文章被收录于专栏:微言码道微言码道

9月底,Swift 5.5正式发布,这是一个相对比较重要的版本,因为它引入了一个新的特性,async/await。

我一直非常关注这个特性,因为几乎在其它所有端都已经全面支持async/await这个特性,这个特性对于异步编码的代码简洁性及普及,具有非常高的价值。

一)

可能还有些人并不是非常清楚async/await是用来做什么的,我简要说明下。

要说async/await,可能要从JavaScript说起。

和Java这种语言不太一样,JavaScript是单线程的,所以它只能设计成异步的。异步的代码和同步的代码在思维上截然不同。举例说明:

在Java下的同步模式

代码语言:javascript
复制
//假设executeSomeBigCal是一个非常耗时的操作,在Java中,我们可以将线程卡在这等待它执行完再返回
dobule result = executeSomeBigCal();
//拿到结果后,再恢复运行,线程得以继续运行
System.out.print("结果是:" + result);

由于JavaScript是单线程的,肯定没法像Java这样,这样就挂了。于是JavaScript是事件回调模式。什么概念呢?

在JavaScript下的实现

代码语言:javascript
复制
executeSomeBigCal(function(result){
  console.log("结果是:" + result)
});

可以看到,它与Java完全不同,它给executeSomeBigCal传递了一个函数,而不是等待executeSomeBigCal返回结果。

在JavaScript中,当executeSomeBigCal执行完成后,再回调执行传入的函数。这样几乎不会阻塞,都是一个执行完,回调下一步,而不是等待一个执行完,再执行下一步。

这就是同步编程与异步编码的根本性不同。由于没有阻塞线程,异步模式下的性能远优于同步阻塞的模式,所以在Java阻塞式的编程中,基本都会遇到一个问题就是大并发下系统的线程不够的问题。

而在异步模式下,由于不阻塞线程,没有这个问题。

前些年非常火的NodeJS之所以性能非常好,原因在于它也是基于JavaScript的,也是异步模式。

异步模式下由于不阻塞线程,虽然性能较好,但一个业务通常不可能只包含一两个过程,任何一个业务可能包含非常多个过程,这就会形成一种非常不好的代码风格。

代码语言:javascript
复制
executeSomeBigCal(function(){
  secondFunction(function(){
    thirdFunction(function(){
      //.....
    });
  });
});

这种代码在风格上极难阅读,所以有一个专门的名词来形容它,就是回调地狱。它简直是可维护代码的天敌,随时代码越来越多,这种代码非常难以阅读与维护。

这个问题在移动端也存在,虽然移动端并不是单线程的,但它也是以异步回调为主,因为毕竟手机性能有限,不可能允许大量线程来运行一个App。

二)

有问题,就一定会有解决方案。

我并不清楚async/await究竟是在哪里第一个引入,但对于JavaScript这个语言,终于有一天引入了async/await这个特性。

这个特性的作用就是:

让异步回调的代码看起来和同步风格一样,以提升代码的可阅读性

有了async/await之后,代码就变成如下这个样式:

代码语言:javascript
复制
const result = await executeSomeBigCal();
console.log("结果是:" + result);

看到没,是不是和同步的代码一样了?

它改善了回调地狱的问题,便得代码更简洁易懂,又保持了异步的机制下的性能优势,它只是让代码样子上看起来像是同步,但实际并未阻塞任何线程。

当然。它也有问题,就是错误处理方面可能需要特别注意,这是后话,在这就不详细表述了。

三)

异步编程这种模式在性能上的优势,引发了较大的关注,虽然主流的Java或是Spring Boot还是保持着传统的线程阻塞模式,但也出现了响应式编程这种模式,就是基于异步机制的模式。

在性能上,异步编程的优势非常明显。

所以它有日渐流行的趋势。

比如Spring基于异步推出了Spring WebFlux,还有Vert.x也比较有知名度,国内的话比如字节跳动开源的CloudWeGo,核心也是使用Netpoll这个高性能的非阻塞I/O异步实现,不过它用的是GO而非Java。

我的myddd-vertx是基于Kotlin与Vert.x的响应式领域驱动框架,它兼具Kotlin带来的优雅简洁与Vert.x带来的异步高效,让编程简直成为一种享受

一旦使用异步非阻塞这种实现,就一定绕不开代码风格这个问题。

所以,在后端开发,移动端开发以及前端开发,都可以使用异步机制来编程了,它在性能上具有极大的优势。而且几乎每个方向都有async/await这个特性。

四)

用实际的代码来做一些说明吧。

代码摘自我在2020年基于Electron开发的一个跨平台软件

代码语言:javascript
复制
    #TypeScript代码
    public static async syncFavors(): Promise<Contact[]> {
        //从网络获取星标联系人,这实质上是一个异步行为
        const favors = await Contact.getNet().fetchFavorContacts();
        if (favors) {
            //存储到数据库中,这也是一个异步操作
            await Contact.getRespository().batchSaveFavors(favors);
        }
        return favors;
    }

代码摘自我的myddd-vertx开源框架,基于Kotlin与Vert.x的响应式领域驱动基础框架

代码语言:javascript
复制
    @Test
    fun testAddComment(vertx: Vertx, testContext: VertxTestContext){
        GlobalScope.launch {
            val comment = createComment()
            //kotin中的await()
            val created = commentRepository.createComment(comment).await()
            testContext.verify {
                Assertions.assertTrue(created.id > 0)
                Assertions.assertEquals(created.id,created.rootCommentId)
                Assertions.assertEquals(created.level,0)
            }
            testContext.completeNow()
        }
    }

而在后端编码中,Java语言本身没有async/await这种风格,但它也提供了FutureTask这种机制,和async/await相似。而且Java的生态中诸如guava也提供了类似的ListenableFuture的机制。

另外在Java生态中,还有非常流行的RxJava这种流式风格的异步编程。也非常值得关注。

Android也是使用的Kotlin,所以风格与上面这个基本类似。

代码语言:javascript
复制
//在Android中使用async及await,实质是基于Kotlin协程
suspend fun fetchTwoDocs() =
    coroutineScope {
        val deferredOne = async { fetchDoc(1) }
        val deferredTwo = async { fetchDoc(2) }
        deferredOne.await()
        deferredTwo.await()
 }

所以,可以看到,在后端,移动端,前端你都可以使用这种async/await这种风格。

五)

唯独iOS的Swift,直到5.5这个版本之前,都不支持这个特性。

在Swfit 5.5版本以前,同样可能存在回调地狱的问题。

代码语言:javascript
复制
func makeSandwich(completionBlock: (result: Sandwich) -> Void) {
    cutBread { buns in
        cutCheese { cheeseSlice in
            cutHam { hamSlice in
                cutTomato { tomatoSlice in
                    let sandwich = Sandwich([buns, cheeseSlice, hamSlice, tomatoSlice] 
                    completionBlock(sandwich))
                }
            }
        }
    }
}

makeSandwich { sandwich in
    eat(sandwich)
}

在支持了async/await特性后,上面的代码可以简化为:

代码语言:javascript
复制
async func cutBread() -> Bread
async func cutCheese() -> Cheese
async func cutHam() -> Ham
async func cutTomato() -> Vegetable

async func makeSandwich() -> Sandwich {
    let bread  = await cutBread()
    let cheese = await cutCheese()
    let ham    = await cutHam()
    let tomato = await cutTomato()
    return Sandwich([bread, cheese, ham, tomato])
}

同样功能的代码在简洁性上的差别是不是天差地别?

五)

当然,就这个回调地狱,也不只是async/await一个解决方案,上面我也提供RxJava这种流式编程风格的方式,也是解决方案的一种。

只不过,相对而说,Rx这种模式,对习惯了同步模式下的编程人员来说,仍然存在学习上的曲线问题,而相比较之下,async/await似乎更容易令人接受与学习,它在代码风格上与同步编程风格很类似。

async/await实质并未提供任何新特性,但它极大的简化了异步编程下的代码简洁性与学习曲线。

就算是在后端,Java与Spring Boot仍然占领主流地位的今天,在国内也出现了字节这样的敢于使用GO这样的异步机制的语言,确实令人耳目一新,说明其在技术创新上走在了国内公司的前列,令人佩服。

我认为不可避免的,异步编程会慢慢成为主流,只是对于国内中小企业来说,考虑到人员招聘与技术试错的成本太高,在这种技术创新上仍然会趋向保守,这个转变过程可能不会那么快。

有两个方向的更易于接受,其一是类似字节这样的大企业,有足够招聘到优秀程序员的能力与资本,再一个就是小型的精英团队或个人,出于对自我的信心与技术的追求,愿意采取异步编程的可能性会比较高。

六)

不管你是从事哪一端的开发,我认为,你需要从现在开始,可以关注异步编程了。特别是对于后端开发的程序员,由于习惯了Java与Spring Boot,改变可能存在一定的阻力。但至少你了解一下总归是没错的。

当然,这不代表我否定同步编程这种风格。

技术只是工具,做为程序员,我们需要评估当前的环境,选择最合适的工具。不能因为习惯了什么某种工具,就不管环境什么场景都要使用这种方式。

这其实是一种自我设限。

我们不能被这种自我设限所阻碍。

如果还不太了解异步编程,希望我这篇文章能引起你的兴趣,让你能开始关注下异步编程,因为有了async/await,异步编程并没有想象的那么复杂了。

如果你不知道从何处开始,可以从我的myddd-vertx开源框架开始了解,基于Kotlin与Vert.x的响应式领域驱动基础框架。访问myddd官网(https://myddd.org)以了解更多

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-10-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微言码道 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一)
  • 二)
  • 三)
  • 四)
  • 五)
  • 五)
  • 六)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档