Swift concurrency 5 — async let的理解与使用

news2024/11/15 15:31:11

在前面的文章中,我们介绍过async/await这两个关键字,也了解了异步方法,在一个Task中,多个加了await的异步方法是顺序执行的,一个接着一个,这个在有些情况下是很好的,比如用户登录,获取token,再获取info信息等。但是有些时候如果有几个不相干的请求想同时都发送出去,然后等他们一起都回来了,再统一处理剩余逻辑,那么此时就要用到async let了。

下面先来看一组代码:

struct AsyncLetDemo: View {
    
    @State private var images: [UIImage] = []
    let columns = [GridItem(.flexible()), GridItem(.flexible())]
    let url = URL(string: "https://picsum.photos/300")!
    
    var body: some View {
        ScrollView {
            LazyVGrid(columns: columns) {
                ForEach(images, id: \.self) { image in
                    Image(uiImage: image)
                        .resizable()
                        .scaledToFit()
                        .frame(height: 150)
                }
            }
        }
        .onAppear {
            Task {
                do {
                    let image1 = try await fetchImage()
                    self.images.append(image1)

                    let image2 = try await fetchImage()
                    self.images.append(image2)

                    let image3 = try await fetchImage()
                    self.images.append(image3)

                    let image4 = try await fetchImage()
                    self.images.append(image4)

                } catch {
                    print("\(error.localizedDescription)")
                }
            }
        }
    }
    
    func fetchImage() async throws -> UIImage {
        do {
            let (data, _) = try await URLSession.shared.data(from: url, delegate: nil)
            if let image = UIImage(data: data) {
                return image
            } else {
                throw URLError(.badURL)
            }
        } catch {
            throw error
        }
    }
}

在上面的代码中,我们通过async/await结合Task的方式请求了4个图片,并在LazyVGrid中显示,按照之前说的逻辑,这4张图片应该会按照顺序显示出来。

请添加图片描述
上一篇文章中说过同一个Task中的异步任务是按照顺序执行的,那么不同的Task是一起执行的,那么稍微修改一下代码:

.onAppear {
    Task {
        do {
            let image1 = try await fetchImage()
            self.images.append(image1)

        } catch {
            print("\(error.localizedDescription)")
        }
    }
    Task {
        do {
            let image2 = try await fetchImage()
            self.images.append(image2)

        } catch {
            print("\(error.localizedDescription)")
        }
    }
    Task {
        do {
            let image3 = try await fetchImage()
            self.images.append(image3)

        } catch {
            print("\(error.localizedDescription)")
        }
    }
    Task {
        do {
            let image4 = try await fetchImage()
            self.images.append(image4)

        } catch {
            print("\(error.localizedDescription)")
        }
    }
}

修改成这样,按理说这些图片应该是基本上同时请求回来了,看下效果:
请添加图片描述
这回效果好多了,但是代码确实是多了很多,如果这样写代码,可能会被骂,哈哈。

下面就来看看今天的重点,async let

async let fetchImage1 = fetchImage()
async let fetchImage2 = fetchImage()
async let fetchImage3 = fetchImage()
async let fetchImage4 = fetchImage()

fetchImage()是我们的请求图片的方法,采用async let定义变量并持有这个方法,比如上面代码。定义完成后fetchImage()是没有立即调用的。而它们的调用也是非常有意思。

let (image1, image2, image3, image4) = await (try fetchImage1, try fetchImage2, try fetchImage3, try fetchImage4)

上面代码等号左边括号内接收返回值,等号右边括号内调用方法,因为fetchImage()是有throws的,所以的加上try,另外这是个async修饰的异步方法,所以得加await,这里面await只需要一个即可。
当上面4个请求都返回后代码才会继续往下走。通过async let我们可以让一组异步方法同时去执行,并且当所有异步方法都返回结果后,程序再继续往下走。

完整代码如下:

.onAppear {
    Task {
        do {
            async let fetchImage1 = fetchImage()
            async let fetchImage2 = fetchImage()
            async let fetchImage3 = fetchImage()
            async let fetchImage4 = fetchImage()
            
            let (image1, image2, image3, image4) = await (try fetchImage1, try fetchImage2, try fetchImage3, try fetchImage4)
            
            self.images.append(contentsOf: [image1, image2, image3, image4])
        } catch {
            print("\(error.localizedDescription)")
        }
        
    }
}

上面的代码是比较理想状态下的,如果某个请求错误了,抛出了异常怎么办,比如下面,我们加了一个方法,使其抛出异常。

func fetchImageWithError() async throws -> UIImage {
    do {
        throw URLError(.badURL)
    } catch {
        throw error
    }
}

调用的地方如下:

.onAppear {
   
    Task {
        do {
            async let fetchImage1 = fetchImage()
            async let fetchImage2 = fetchImage()
            async let fetchImage3 = fetchImage()
            async let fetchImageWithError = fetchImageWithError()
            
            let (image1, image2, image3, image4) = await (try fetchImage1, try fetchImage2, try fetchImage3, try fetchImageWithError)
            
            self.images.append(contentsOf: [image1, image2, image3, image4])
        } catch {
            print("\(error.localizedDescription)")
        }
    }
}

上面代码中fetchImageWithError肯定会抛异常了,那么代码就直接到catch闭包里面了,其他三个正常返回的请求也就失效了。
请添加图片描述
从效果图可以看出,控制台有错误打印出来,UI上并没有显示出成功的三个图片。

解决办法也比较简单,在fetchImageWithError方法前将try改成try?,忽略它的异常报错,这样也会引起一个问题,那么就是在向数组里添加图片的时候会报错,因为改成try?修饰的fetchImageWithError方法返回了一个可选的UIImage,所以我们需要判断这个UIImage是否存在,然后在加入图片数组中。

.onAppear {
   
    Task {
        do {
            async let fetchImage1 = fetchImage()
            async let fetchImage2 = fetchImage()
            async let fetchImage3 = fetchImage()
            async let fetchImageWithError = fetchImageWithError()
            
            let (image1, image2, image3, image4) = await (try fetchImage1, try fetchImage2, try fetchImage3, try? fetchImageWithError)
            
            self.images.append(contentsOf: [image1, image2, image3, image4 ?? UIImage()])
        } catch {
            print("\(error.localizedDescription)")
        }
        
    }
}

上面代码中再给图片数组添加图片的时候,第四个可选的图片采用了??设置了缺省值,也可以不这样做。

如果你的异步方法里面没有throws关键字,那么在调用的时候前面就不需要加try了。

总体来说async let的用法还是比较简单的,可以让我们将多个异步方法同时发送出去,并在所有结果都返回后再执行后续的代码,这在项目中的某些场景还是非常实用的。感兴趣的小伙伴赶紧用起来吧。

最后在说一句,如果你有很多歌,比如几十个异步请求同时发送出去,然后等待统一返回结果,那不建议用async let了,还是来看看下一篇文章吧。

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

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

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

相关文章

element-ui 表单Cannot read property ‘indexOf‘ of undefined

在使用resetField 报错Cannot read property ‘indexOf’ of undefined 复现说明 报错展示 解决方案 用clearValidate()替换resetFields()

Mysql基础练习题 183.找出所有从不点任何东西的顾客 (力扣)

183.找出所有从不点任何东西的顾客 建表插入数据: #插入数据,顾客表和订单表 Create table If Not Exists Customers (id int, name varchar(255)) Create table If Not Exists Orders (id int, customerId int) Truncate table Customers insert into…

Java面试宝典-java基础09

Java面试宝典-java基础09 81、String,Stringbuffer,StringBuilder 的区别82、Comparator 与 Comparable 有什么区别83、说说反射用途及实现原理,Java 获取反射三种方法84、&和&&的区别85、Java 创建对象有几种方式86、如何将 GB2312 编码字符串转换为 ISO-8859-…

一起学Java(11)-[日志篇]教你分析SLF4J源码,掌握Logger接口实现类加载原理

最近各种事情很忙,今天继续。在第十篇(一起学Java(10)-为项目引入Log框架(Log篇二-引入SLF4J接口层框架))中,我们为项目(https://github.com/lihongzheshuai/java-all-in-one)引入了SLF4J和Logback框架,按计划通过阅读源码研究下SLF4J的实现原…

如何在忘记密码或 ID 的情况下解锁 iPhone 15

您是否曾经因为忘记了 iPhone 密码而陷入困境,或者您是否多次错误地输入了屏幕时间密码并发现自己被锁定在 iPhone 之外? 被锁定和拒绝访问您的 iPhone 可能很常见,尤其是在您尚未配置 Face ID 的情况下。或者,如果 Face ID 无法正…

【机器学习】广义线性模型和一般线性模型的联系和区别以及如何选择合适的链接函数

引言 广义线性模型(Generalized Linear Models,简称GLM)和一般线性模型(通常指的是线性回归模型)都是统计建模中常用的工具,但它们在假设、适用范围和模型结构上有所不同 文章目录 引言一、广义线性模型和一…

黑马程序员Python机器学习|1机器学习概述

一 人工智能概述 1.机器学习和人工智能,深度学习的关系 2.机器学习可以做什么 翻译,智能客服。 二 什么是机器学习 1.机器学习的定义 从数据中学习,学习后得出的结论是模型(规律),通过这个规律去解决问题&a…

网络安全教程初级简介

随着技术的发展和信息池的增加,信息系统迫切需要建立网络安全措施,以保护输入这些系统的信息。网络安全是任何组织(从小型初创公司到大型跨国组织)的必备条件。 网络安全包括一系列技术、流程和实践,用于保护网络、设…

测试面试题,自动化测试与性能测试篇(附答案)

本系列文章总结归纳了一些软件测试工程师常见的面试题,主要来源于个人面试遇到的、网络搜集(完善)、工作日常讨论等,分为以下十个部分,供大家参考。如有错误的地方,欢迎指正。有更多的面试题或面试中遇到的…

直播电商如何实现精细化运营,破除流量互卷的困境?

在当今电商行业的激烈竞争中,流量的获取与维持已成为品牌和商家关注的焦点。然而,随着市场逐渐饱和,流量成本不断攀升,传统的流量获取方式已不再可持续,精细化运营才是电商企业脱颖而出的关键。 本文将深入探讨电商行…

聚类算法k-means(手撕和调用skl)

定义 K均值聚类(k-means clustering)算法是一种常用的、基于原型的聚类算法,简单、直观、高效。其步骤为: 第一步:根据事先已知的聚类数,随机选择若干样本作为聚类中心,计算每个样本与每个聚类…

大数据及人工智能产品应该如何测试?

当今社会,人工智能的发展非常快,自从2016年AlphaGo战胜了世界围棋冠军李世石之后,人工智能的发展,特别是以深度学习为代表的人工智能的发展到了一个高速发展的阶段。 现在人工智能的发展已经渗透到了我们生活的方方面面&#xff…

如何对 PDF 进行密码保护

保护机密文件非常关键。对 PDF 进行密码保护的策略是在未经授权的访问时增加一层安全保护。在处理高度机密的文档或个人数据时,使用密码保护它们是一个好主意。 也就是说,如果您担心如何在笔记本电脑和台式机上使用密码保护 PDF, 以及您是否…

深入浅出通信原理 | 单位冲激响应和时域卷积定理

微信公众号上线,搜索公众号小灰灰的FPGA,关注可获取相关源码,定期更新有关FPGA的项目以及开源项目源码,包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 本节目录 一、单位冲激响应 1、离…

rpm安装出现警告: 密钥 ID f4a80eb5: NOKEY的解决办法

当我们使用rpm安装时有时会出现警告:/mnt/Packages/ethtool-4.8-9.el7.x86_64.rpm: 头V3 RSA/SHA256 Signature, 密钥 ID f4a80eb5: NOKEY 这是因为各个软件之间总会存在一些依赖关系,所以才会发出警告,这时候我们只需要在后面加上"–…

C++面向对象高级开发A

一、概述 目标:培养正规、大气的编程习惯;学习面向对象设计 Object Based(基于对象):以良好的方式编写Cclass class without pointer members【示例:Complex类】class with pointer members【示例&#xff…

挂个人-CSDN Java优秀内容博主rundreamsFly抄袭

事件起因 今天点开自己的CSDN博客,发现给我推了一篇文章抄袭我自己昨天18点发的文章。 就是这篇,一字不差,博主昵称是:rundreamsFly,账号是rundreams。 抄袭者文章 发布于2024-8-26 19:37:41秒,比我发布…

C语言穿墙迷宫

目录 开头程序程序的流程图程序游玩的效果下一篇博客要说的东西 开头 大家好&#xff0c;我叫这是我58。 程序 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <stdlib.h> #include <time.h> #include <Windows.h> void printmaze…

Axure团队协作功能详解:从创建到管理的全流程

Axure RP 支持团队协作&#xff0c;通过创建团队项目&#xff0c;多个团队成员可以同时在同一个项目上进行编辑和管理。以下是使用 Axure 进行团队协作的详细步骤&#xff1a; Axure 使用地址 1. 创建团队项目 打开 Axure RP&#xff0c;并在菜单栏中选择 “Team” > “Cr…

【6678专题】-点亮LED灯(寄存器方式)

本章需要参考的资料为 《General Purpose Input Output (GPIO) User Guide.pdf》&#xff0c;具体在创龙资料文件夹目录下D:\JYTL\12DSP_FPGA\08_文档\创龙\TL6678ZH-EVM_V1.5\TL6678ZH-EVM_V1.5\6-开发参考资料\数据手册\核心板元器件\DSP\Technical Reference Manual 《Multi…