0

0

异步处理Uni中的元素:从问题到解决方案

聖光之護

聖光之護

发布时间:2025-09-27 12:12:18

|

251人浏览过

|

来源于php中文网

原创

异步处理Uni<List>中的元素:从问题到解决方案
中的元素:从问题到解决方案 " />

本文深入探讨了如何在响应式编程框架中异步处理Uni类型中的每个元素。针对常见的异步操作生命周期管理挑战,文章详细介绍了两种核心策略:一是通过将Uni转换为Multi并利用transformToUniAndMerge实现并发处理,并结合测试上下文确保异步流的完整性;二是通过collect().asList().await().indefinitely()阻塞式等待所有异步操作完成并收集结果。文章旨在提供清晰的指导和示例,帮助开发者有效管理和执行列表元素的并发异步处理。

理解Uni中的异步处理挑战

quarkus或类似的响应式编程环境中,uni>表示一个最终会发出一个list的异步操作。当我们需要对这个列表中的每个元素执行另一个异步操作时,常见的挑战是如何确保所有这些独立的异步操作都能被正确地触发、执行并等待其完成,而不会因为主线程的提前退出而导致部分操作未能完成。

初始尝试可能涉及将Uni>通过map操作转换为List>,然后使用Uni.join().all(unis).andCollectFailures()来合并这些Uni。然而,如果后续直接使用.subscribe().with(System.out::println),由于subscribe是非阻塞的,程序可能会在所有异步任务完成之前就退出,从而给人一种只有第一个元素被处理的错觉。核心问题在于如何管理这些异步操作的生命周期,确保在所有任务真正完成之前,程序不会终止。

解决方案一:利用Multi进行流式处理与异步生命周期管理

为了高效且可靠地处理Uni中的每个元素,并确保所有异步操作都能在主程序退出前完成,我们可以将Uni转换为Multi,从而将列表中的每个元素作为独立的流事件来处理。结合Vert.x Unit等测试框架的异步上下文,可以优雅地管理异步流的生命周期。

核心思路

  1. Uni转换为Multi:Uni.onItem().transformToMulti(Multi.createFrom()::iterable)是关键一步,它将一个包含列表的Uni转换为一个可以逐个发出列表元素的Multi。
  2. 并发处理每个元素:onItem().transformToUniAndMerge(s -> ...)允许我们为Multi中的每个元素创建一个新的Uni(代表该元素的异步处理),然后将这些独立的Uni的发射合并回一个单一的Multi。transformToUniAndMerge的特性是它会并发地订阅和处理这些内部的Uni。
  3. 异步生命周期管理:在测试环境中,Vert.x Unit的TestContext和Async对象提供了一种机制来明确地通知测试框架何时异步操作完成。onTermination().invoke()可以在流终止时执行回调,用于标记异步任务的完成状态。

示例代码 (结合Vert.x Unit)

import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.time.Duration;
import java.util.List;
import java.util.Random;

@RunWith(VertxUnitRunner.class)
public class AsyncListProcessingExample {

    @Test
    public void testAsyncListProcessing(TestContext context) {
        Random random = new Random();
        // 用于在流终止时通知测试框架
        Async async = context.async();

        Uni.createFrom()
                .item(List.of("a", "b", "c")) // 原始的Uni>
                // 将 Uni> 转换为 Multi,每个字符串作为独立的流事件
                .onItem().transformToMulti(Multi.createFrom()::iterable)
                // 对 Multi 中的每个字符串元素进行异步处理
                .onItem().transformToUniAndMerge(s ->
                        // 为每个元素创建一个 Uni,模拟异步操作(这里是随机延迟)
                        Uni.createFrom().item(s)
                                .onItem().delayIt().by(Duration.ofMillis((random.nextInt(5) + 1) * 1000))
                )
                // 在流终止时执行回调,无论成功或失败
                .onTermination().invoke((throwable, aBoolean) -> {
                    if (throwable != null) {
                        context.fail(throwable); // 如果有异常,测试失败
                    } else {
                        async.complete(); // 所有异步操作完成,通知测试通过
                    }
                })
                // 订阅 Multi,处理每个完成的元素
                .subscribe()
                .with(s -> System.out.println("Printing: " + s));
    }
}

注意事项

  • 测试上下文:VertxUnitRunner和TestContext是测试框架提供的,用于确保异步测试的正确执行。在生产代码中,你可能需要根据实际应用场景(如Web请求的生命周期、后台服务的启动/停止)来管理异步操作的完成。
  • transformToUniAndMerge:这个操作符是实现并发处理的关键。它会为每个流元素创建一个Uni,并并发地订阅这些Uni,然后将它们的结果按完成顺序合并到下游。

解决方案二:阻塞式收集所有结果

在某些场景下,我们可能需要等待所有异步操作完成后,才继续执行后续的同步代码,或者需要将所有异步处理的结果收集到一个列表中。Mutiny提供了阻塞式等待和收集结果的机制。

PHP经典实例(第二版)
PHP经典实例(第二版)

PHP经典实例(第2版)能够为您节省宝贵的Web开发时间。有了这些针对真实问题的解决方案放在手边,大多数编程难题都会迎刃而解。《PHP经典实例(第2版)》将PHP的特性与经典实例丛书的独特形式组合到一起,足以帮您成功地构建跨浏览器的Web应用程序。在这个修订版中,您可以更加方便地找到各种编程问题的解决方案,《PHP经典实例(第2版)》中内容涵盖了:表单处理;Session管理;数据库交互;使用We

下载

核心思路

  1. Uni转换为Multi:与解决方案一相同,首先将Uni转换为Multi。
  2. 并发处理并收集:同样使用onItem().transformToUniAndMerge()进行并发处理。
  3. 阻塞等待并收集结果:collect().asList().await().indefinitely()是一个强大的组合。collect().asList()会将Multi发出的所有元素收集到一个List中,并返回一个Uni。接着,.await().indefinitely()会阻塞当前线程,直到这个Uni发出其结果(即收集到的列表),或者发生错误。

示例代码 (阻塞式收集)

import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;

import java.time.Duration;
import java.util.List;
import java.util.Random;

public class BlockingListProcessingExample {

    public static void main(String[] args) {
        Random random = new Random();

        List results = Uni.createFrom()
                .item(List.of("a", "b", "c")) // 原始的Uni>
                // 将 Uni> 转换为 Multi
                .onItem().transformToMulti(Multi.createFrom()::iterable)
                // 对 Multi 中的每个字符串元素进行异步处理
                .onItem().transformToUniAndMerge(s -> {
                    final int duration = (random.nextInt(5) + 1) * 1000;
                    // 为每个元素创建一个 Uni,模拟异步操作
                    return Uni.createFrom().item(s)
                            .onItem().delayIt().by(Duration.ofMillis(duration))
                            .invoke(() -> System.out.println("Letter: " + s + ", duration in ms: " + duration));
                })
                // 订阅 Multi,处理每个完成的元素
                .onItem().invoke(s -> System.out.println("Printing: " + s))
                // 收集所有结果到一个列表中
                .collect().asList()
                // 阻塞当前线程,直到所有结果都被收集完毕
                .await().indefinitely();

        System.out.println("All results collected: " + results);
    }
}

注意事项

  • 阻塞操作:.await().indefinitely()会阻塞当前线程。在响应式应用中,应尽量避免在I/O线程或事件循环线程中执行阻塞操作,因为它会降低系统的吞吐量。此方法更适用于主方法、命令行工具或需要在特定点同步等待结果的场景。
  • 错误处理:如果任何一个内部的Uni失败,整个流会终止,并且await()方法会抛出异常。你可以使用onFailure().recoverWith(...)等操作符来处理错误。

总结

在Mutiny中异步处理Uni中的元素,关键在于将Uni有效地转换为Multi,以便对每个元素进行流式处理。

  • 对于需要非阻塞、响应式处理且在测试环境中管理异步操作生命周期的场景,onItem().transformToMulti(Multi.createFrom()::iterable)结合onItem().transformToUniAndMerge() 是理想选择,并通过测试上下文(如Vert.x Unit的Async)来确保所有异步任务的完成。
  • 当需要在某个点同步等待所有异步操作完成并收集其结果时,collect().asList().await().indefinitely() 提供了一种方便的阻塞式解决方案,但需注意其对性能和响应性的潜在影响。

理解这两种模式及其适用场景,能够帮助开发者更灵活、高效地构建基于Mutiny的异步应用。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

315

2023.08.02

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

175

2023.11.23

java中void的含义
java中void的含义

本专题整合了Java中void的相关内容,阅读专题下面的文章了解更多详细内容。

97

2025.11.27

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

480

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

480

2023.08.10

golang map内存释放
golang map内存释放

本专题整合了golang map内存相关教程,阅读专题下面的文章了解更多相关内容。

74

2025.09.05

golang map相关教程
golang map相关教程

本专题整合了golang map相关教程,阅读专题下面的文章了解更多详细内容。

28

2025.11.16

golang map原理
golang map原理

本专题整合了golang map相关内容,阅读专题下面的文章了解更多详细内容。

59

2025.11.17

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

12

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.5万人学习

C# 教程
C# 教程

共94课时 | 6.6万人学习

Java 教程
Java 教程

共578课时 | 45.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号