Swift concurrency 2 — async await的理解与使用

news2024/9/24 11:26:28

async / await

将函数标记为async会告诉Swift编译器该函数是异步执行的,是可以挂起的。await关键字标记了这些挂起点。当一个函数在await调用时被挂起时,它所执行的线程可以用来执行其他工作。当等待的工作完成时,运行时可以恢复函数的执行。
比如下面这段代码:

func fetchServerData() async throws -> Data {
    let (data, _) = try await URLSession.shared.data(from: URL(string: "https://xxxxx")!)
    return data
}

在函数fetchServerData()中使用了async修饰,从而告诉编译器这是个异步方法,可以挂起。在方法里面请求的时候使用了await,因此在网络请求的时候,不会立即执行return,此时的函数被挂起了,也可以认为被暂停了,只有请求返回结果后,运行时再继续执行该方法剩余部分。

还有一点需要注意的是被async标记的函数/方法只能在异步上下文中调用,例如Task或者其他异步函数中调用。

Task {
    let data = try await viewModel.fetchServerData()
}

async:修饰的方法或函数是异步的。只能在异步上下文中调用,需要和await搭配使用。
await:在调用async修饰的方法时,需要在前面使用await,表示代码在等待异步的方法或函数返回时可能会暂停执行。使用它可以暂停执行,直到异步方法返回结果。

除了用在函数或方法中,async/await还可以用在计算属性中:

var myProperty: String {
    get async {
        return ""
    }
}

上面return了一个空字符串,实际中,可能需要一些耗时的操作后才能返回。
那么在调用的地方调用函数方法一样,如下:

Task {
    let myProperty = await viewModel.myProperty
}

上一篇文章也就说如果有多个情况相互依赖,如果我们采用逃逸闭包(@escaping closure)的方法,会嵌套很多层,即使使用的throws抛出异常,代码也是比较难维护了,遇到这样的代码,除非是自己写的,否则一般都不太情愿去维护。

现在我们模拟一个场景,延迟2秒模拟网络数据返回的情况,我们嵌套几层看看

func addTitle() {
    // 1
    let title1 = "title1 \n\(Thread.current)"
    dataArray.append(title1)
    
    // 2
    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
        let title2 = "title2 \n\(Thread.current)"
        self.dataArray.append(title2)
        
        // 3
        DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
            let title3 = "title3 \n\(Thread.current)"
            DispatchQueue.main.async {
                self.dataArray.append(title3)
                // 4
                let title4 = "title4 \n\(Thread.current)"
                self.dataArray.append(title4)
            }
        }
    }
}

上面代码每个2秒向数组添加一个元素并显示在界面上,为了不阻塞主线程,我们用了很多异步操作,采用了DispatchQueue.main.asyncAfterDispatchQueue.global().asyncAfter等方法,并且最终还要回到主线程刷新UI。比如在第三步如果用到了DispatchQueue.global().asyncAfter,那么还要调用DispatchQueue.main.async回到主线程,整体来说代码嵌套比较多。这里还特意打印了不同时期的线程情况,如下:
在这里插入图片描述

现在改成用asyncawait组合再试试:

func addAuthor() async {
    let author1 = "author1 \n\(Thread.current)"
    await MainActor.run {
        self.dataArray.append(author1)
    }
    
    try? await Task.sleep(nanoseconds: 2_000_000_000)
    
    let author2 = "author2 \n\(Thread.current)"
    await MainActor.run {
        self.dataArray.append(author2)
    }
    
    try? await Task.sleep(nanoseconds: 2_000_000_000)
    
    let author3 = "author3 \n\(Thread.current)"
    await MainActor.run {
        self.dataArray.append(author3)
    }
    
    try? await Task.sleep(nanoseconds: 2_000_000_000)
    
    let author4 = "author4 \n\(Thread.current)"
    await MainActor.run {
        self.dataArray.append(author4)
    }
}

采用asyncawait组合代码就好看多了,至少是从上往下看,而不是从外往里看了。代码也是从上至下一步一步执行的。
这里面采用了try? await Task.sleep(nanoseconds: 2_000_000_000)模拟2秒延迟的情况。

addAuthor()方法使用了async修饰,表示这个方法是一个异步方法,里面会有await将方法挂起来。因为是异步方法,所以每次在向dataArray添加数据刷新UI的时候,都需要回到主线程,之前回到主线程一般采用的是DispatchQueue.main.async,这次采用了MainActor,后面文章会涉及到这个,这里就不阐述了,苹果推出了MainActor,也意味着想让我们少用DispatchQueue.main.async这类代码,或者说给开发者更多了选择。

addAuthor()是异步的,那是不是方法里面都是在同一个异步线程呢?看看打印输出就知晓了。
在这里插入图片描述
通过运行打印可以看出,这里面用到了好几个线程,每次遇到延迟代码后都会切换一个线程。
所以笔者认为async修饰的方法提供了一个异步环境,但是处理里面的事件不一定是一个线程在处理,当遇到await等待的时候,方法被挂起了,线程也去处理别的事情了,当方法有返回数据的时候,系统再找一个空闲的异步线程接着处理方法的返回值值及后续的事件。

小结一下

async/await的组合能够帮助发开着写出更容易理解,更加健壮并且易维护的异步编程代码,它的出现将会替代以前的异步编程逻辑,对开发者更加友好。
asyncawait是组合使用的,调用被async修饰的方法的时候,需要在异步上下文中执行,并且在调用方法的前面加上await关键字。
如果需要提供一个上下文的环境,我们可以采用Task,其闭包内提供了一个异步环境。
另外不管怎么调用,最终都要回归到UI上,切记要调用主线程代码。

如果上述文章内容有不当或错误之处,还请不吝赐教。

最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2081127.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络实用工具

ping 测试与目标主机的连通性 显示解释www.a.shifen.com (14.215.177.39)ping目标主机的域名和IP(ping会自动将域名转换为IP)56(84)不带包头的包大小和带包头的包大小icmp_seq1ping序列,从1开始如果数字不是顺序递增即有丢包ttl54数据包剩余生存时间, 默…

Python | Leetcode Python题解之第378题有序矩阵中第K小的元素

题目&#xff1a; 题解&#xff1a; class Solution:def kthSmallest(self, matrix: List[List[int]], k: int) -> int:n len(matrix)def check(mid):i, j n - 1, 0num 0while i > 0 and j < n:if matrix[i][j] < mid:num i 1j 1else:i - 1return num > …

MS sqlserver备份软件 SQLBackupAndFTP

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目…

Linux—— 配置ssl安全证书

一、实验设计 二、实验配置过程 [studentbogon ~]$ su - root Password: [rootbogon ~]# dnf -y install nginx [rootbogon ~]# vim /etc/selinux/config [rootbogon ~]# setenforce 0 [rootbogon ~]# systemctl stop firewalld 清空防火墙规则 [rootbogon ~]# iptables -F ^C…

CHARLS数据库挖掘系列教程(1)---数据库下载

CHARLS 是一项具备中国大陆 45 岁及以上人群代表性的追踪调查&#xff0c;旨在建设一个高质量的公共微观数据库&#xff0c;采集的信息涵盖社会经济状况和健康状况等多维度的信息&#xff0c;以满足老龄科学研究的需要。 为利用国际上最佳的数据采集方式&#xff0c;并确保研究…

YOLOv8实例分割推理流程及Python代码

1.YOLOv8_Seg推理流程 2.YOLOv8_Seg推理代码 3.全部代码 """ yolov8:目标检测推理代码 python """ import torch import cv2 import numpy as np import onnxruntime as ort import os import torch.nn.functional as F def xywh2xyxy(x):"…

【K8S 基本概念】Kubernets的架构和核心概念及集群搭建

一、Kubernets 1.作用&#xff1a;用于自动部署扩展以及管理容器化部署的应用程序&#xff0c;半开源&#xff0c;k8s的底层是基于谷歌go语言开发的&#xff0c;负责自动化运维管理多个容器化的应用的集群&#xff0c;容器编排框架的工具。现在使用的版本1.18-1.24&#xff0c…

开放式耳机怎么戴?佩戴舒适在线的几款开放式耳机分享

开放式耳机的佩戴方式与传统的入耳式耳机有所不同&#xff0c;它采用了一种挂耳式的设计&#xff0c;提供了一种新颖的佩戴体验&#xff0c;以下是开放式耳机的佩戴方式。 1. 开箱及外观&#xff1a;首先&#xff0c;从包装盒中取出耳机及其配件&#xff0c;包括耳机本体、充电…

java-Mybatis框架02

1.#{} 和${}区别 #{} 是占位符&#xff0c;是采用编译方式向sql中传值&#xff0c;可以防止sql注入&#xff0c;如果往sql中传值&#xff0c;使用#{}${} 是将内容直接拼接到sql语句中&#xff0c;一般不用于向sql中传值&#xff0c;一般用于向sql中动态传递列名。区别&#xff…

电控专业课程已开放

凌鸥学园旨在为电机控制爱好者提供学习交流成长平台&#xff0c;缓解电控行业人才缺口扩大趋势&#xff0c;共同打造繁荣行业生态。我们将全面整合LKS MCU相关资料&#xff0c;包括开发环境、应用笔记、教学视频、学习指导等&#xff0c;并设有专业课程。 通过凌鸥学园的专业体…

【RabbitMQ】快速上手

目 录 一. RabbitMQ 安装二. RabbitMQ 核心概念2.1 Producer 和 Consumer2.2 Connection 和 Channel2.3 Virtual host2.4 Queue2.5 Exchange2.6 RabbitMQ 工作流程 三. AMQP四. web界面操作4.1 用户相关操作4.2 虚拟主机相关操作 五. RabbitMQ 快速入门5.1 引入依赖5.2 编写生产…

简单的jar包解压class文件修改再编译成jar包

简单的jar包解压class文件修改再编译成jar包 1. 需求 我们公司有一个项目演示的环境&#xff0c;这个环境是我们公司其他组的项目&#xff0c;我们组只有这个项目的前端&#xff0c;jar&#xff0c;以及部分数据库结构表信息&#xff0c;现在我们已经启动服务可以正常访问&am…

Python中PDF文件的编辑与创建

目录 安装必要的库 编辑现有PDF文件 合并PDF文件 拆分PDF文件 添加水印 注意 创建新的PDF文件 使用reportlab创建PDF 使用Spire.PDF for Python创建PDF 结论 在数字化办公和学习环境中&#xff0c;PDF&#xff08;Portable Document Format&#xff09;文件因其跨平台…

【免费分享】1982-2015华北平原农田蒸散发数据集

华北平原是中国最重要的产粮基地之一&#xff0c;然而该地区水资源缺乏、供需矛盾突出。 在全球气候变化及用水需求日益增加的背景下&#xff0c; 该地区水循环过程变得愈加脆弱。 因此如何准确估算蒸散发、 掌握蒸散发的时空变化规律&#xff0c; 进而合理配置水资源、提高农业…

Lua收集请求日志

Kafka搭建 单机版的kafka搭建非常简单&#xff0c;不过我们今天采用Docker搭建kafka。Kafka使用Zookeeper存储Consumer、Broker信息&#xff0c;安装kafak的时候&#xff0c;需要先安装Zookeeper。 Zookeeper安装&#xff1a; docker run -d --name zookeeper -p 3181:3181 …

《网络安全自学指南》

《网络安全自学教程》 《网络安全自学》 1、网络协议安全1.1、OSI七层模型1.2、TCP/IP协议栈1.3、Wireshark使用1.4、802.1x协议1.5、ARP协议1.6、ARP欺骗1.7、IP协议1.8、ICMP协议1.9、TCP协议1.10、SYN Flood1.11、SSL协议1.12、HTTP协议1.13、DHCP协议 2、操作系统安全2.1、…

<数据集>TACO垃圾识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;6004张 标注数量(xml文件个数)&#xff1a;6004 标注数量(txt文件个数)&#xff1a;6004 标注类别数&#xff1a;18 标注类别名称&#xff1a;[Crankshaft, Centrifugal_body, Washer_container, Circlip_containe…

保存和加载工作区变量

目录 保存工作区变量 加载工作区变量 查看 MAT 文件的内容 在 MATLAB 会话之间并不保留工作区。退出 MATLAB 时&#xff0c;工作区清除。但是&#xff0c;您可以将当前工作区中的任何或所有变量保存到 MAT 文件(.mat)中。之后&#xff0c;只需加载保存的 MAT 文件&#xff0…

工业设备定位激光器在工业领域的应用

在日新月异的工业制造领域&#xff0c;技术的每一次飞跃都深刻影响着生产效率和产品质量的双重提升。其中&#xff0c;工业设备定位激光器作为现代工业自动化的核心元件之一&#xff0c;正以其高精度、高效率和广泛的应用范围&#xff0c;引领着制造业的智能化转型。接下来给大…

<PLC><算法>使用汇川eazy521系列PLC,如何使用LiteST语言对乱序数组进行排序?(冒泡法)

前言 本系列是关于PLC相关的博文&#xff0c;包括PLC编程、PLC与上位机通讯、PLC与下位驱动、仪器仪表等通讯、PLC指令解析等相关内容。 PLC品牌包括但不限于西门子、三菱等国外品牌&#xff0c;汇川、信捷等国内品牌。 除了PLC为主要内容外&#xff0c;PLC相关元器件如触摸屏…