日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

回调地狱和反应模式

發(fā)布時間:2023/12/3 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 回调地狱和反应模式 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

我可以更好地了解a的用途的一種方式
基于反應(yīng)流的方法是它簡化了無阻塞IO調(diào)用的方式。

這篇文章將快速講解進(jìn)行同步遠(yuǎn)程調(diào)用所涉及的那種代碼,然后說明如何在非阻塞IO中分層,盡管在資源(尤其是線程)的使用方面非常高效,但會帶來稱為回調(diào)地獄的復(fù)雜性,并且基于反應(yīng)流的方法如何簡化編程模型。

目標(biāo)服務(wù)

由于我將編寫一個客戶呼叫,因此代表城市詳細(xì)信息的目標(biāo)服務(wù)有兩個端點。 當(dāng)使用uri類型的“ / cityids”調(diào)用時返回一個城市ID列表,并且示例結(jié)果如下所示:

[ 1 , 2 , 3 , 4 , 5 , 6 , 7 ]

端點返回給定城市ID的城市的詳細(xì)信息,例如,當(dāng)使用ID 1 –“ / cities / 1”進(jìn)行調(diào)用時:

{ "country" : "USA" , "id" : 1 , "name" : "Portland" , "pop" : 1600000 }

客戶的責(zé)任是獲取城市ID的列表,然后為每個城市ID獲取城市的詳細(xì)信息并將其放到城市列表中。

同步通話

我正在使用Spring Framework的RestTemplate進(jìn)行遠(yuǎn)程調(diào)用。 Kotlin函數(shù)獲取城市ID列表如下所示:

private fun getCityIds(): List<String> { val cityIdsEntity: ResponseEntity<List<String>> = restTemplate .exchange( " http://localhost: $localServerPort/cityids" , HttpMethod.GET, null , object : ParameterizedTypeReference<List<String>>() {}) return cityIdsEntity.body!! }

并獲取城市的詳細(xì)信息:

private fun getCityForId(id: String): City { return restTemplate.getForObject( " http://localhost: $localServerPort/cities/$id" , City:: class .java)!! }

給定這兩個功能,可以很容易地將它們組合起來,以便返回城市列表:

val cityIds: List<String> = getCityIds() val cities: List<City> = cityIds .stream() .map<City> { cityId -> getCityForId(cityId) } .collect(Collectors.toList()) cities.forEach { city -> LOGGER.info(city.toString()) }

該代碼非常易于理解,但是涉及8個阻塞調(diào)用–

1.獲取7個城市ID的列表,然后獲取每個城市的詳細(xì)信息 2.獲取七個城市中每個城市的詳細(xì)信息

這些調(diào)用中的每一個都會位于不同的線程上。

結(jié)合使用非阻塞IO和回調(diào)

我將使用一個名為AsyncHttpClient的庫來進(jìn)行非阻塞IO調(diào)用。

進(jìn)行遠(yuǎn)程調(diào)用時,AyncHttpClient返回一個ListenableFuture類型。

val responseListenableFuture: ListenableFuture<Response> = asyncHttpClient .prepareGet( " http://localhost: $localServerPort/cityids" ) .execute()

可以將回調(diào)附加到可監(jiān)聽的將來,以便在可用時對響應(yīng)進(jìn)行操作。

responseListenableFuture.addListener(Runnable { val response: Response = responseListenableFuture.get() val responseBody: String = response.responseBody val cityIds: List<Long> = objectMapper.readValue<List<Long>>(responseBody, object : TypeReference<List<Long>>() {}) .... }

給定cityids列表,我想獲取城市的詳細(xì)信息,因此從響應(yīng)中,我需要進(jìn)行更多的遠(yuǎn)程調(diào)用,并為每個調(diào)用附加一個回調(diào),以按照以下方式獲取城市的詳細(xì)信息:

val responseListenableFuture: ListenableFuture<Response> = asyncHttpClient .prepareGet( " http://localhost: $localServerPort/cityids" ) .execute() responseListenableFuture.addListener(Runnable { val response: Response = responseListenableFuture.get() val responseBody: String = response.responseBody val cityIds: List<Long> = objectMapper.readValue<List<Long>>(responseBody, object : TypeReference<List<Long>>() {}) cityIds.stream().map { cityId -> val cityListenableFuture = asyncHttpClient .prepareGet( " http://localhost: $localServerPort/cities/$cityId" ) .execute() cityListenableFuture.addListener(Runnable { val cityDescResp = cityListenableFuture.get() val cityDesc = cityDescResp.responseBody val city = objectMapper.readValue(cityDesc, City:: class .java) LOGGER.info( "Got city: $city" ) }, executor) }.collect(Collectors.toList()) }, executor)

這是一段粗糙的代碼,在一個回調(diào)中有一組回調(diào),很難對此進(jìn)行推理和理解,因此被稱為回調(diào)地獄。

在Java CompletableFuture中使用非阻塞IO

通過返回Java的CompletableFuture作為返回類型而不是ListenableFuture,可以對代碼進(jìn)行一些改進(jìn)。 CompletableFuture提供允許修改返回的返回類型的運(yùn)算符。

例如,考慮獲取城市ID列表的函數(shù):

private fun getCityIds(): CompletableFuture<List<Long>> { return asyncHttpClient .prepareGet( " http://localhost: $localServerPort/cityids" ) .execute() .toCompletableFuture() .thenApply { response -> val s = response.responseBody val l: List<Long> = objectMapper.readValue(s, object : TypeReference<List<Long>>() {}) l } }

在這里,我使用“ thenApply”運(yùn)算符將“ CompletableFuture <Response>”轉(zhuǎn)換為“ CompletableFuture <List <Long >>”

并類似地獲得城市的詳細(xì)信息:

private fun getCityDetail(cityId: Long): CompletableFuture<City> { return asyncHttpClient.prepareGet( " http://localhost: $localServerPort/cities/$cityId" ) .execute() .toCompletableFuture() .thenApply { response -> val s = response.responseBody LOGGER.info( "Got {}" , s) val city = objectMapper.readValue(s, City:: class .java) city } }

這是基于回調(diào)方法的改進(jìn),但是,CompletableFuture缺少足夠的運(yùn)算符,例如,在這種特定情況下,需要將所有城市詳細(xì)信息放在一起:

val cityIdsFuture: CompletableFuture<List<Long>> = getCityIds() val citiesCompletableFuture: CompletableFuture<List<City>> = cityIdsFuture .thenCompose { l -> val citiesCompletable: List<CompletableFuture<City>> = l.stream() .map { cityId -> getCityDetail(cityId) }.collect(toList()) val citiesCompletableFutureOfList: CompletableFuture<List<City>> = CompletableFuture.allOf(*citiesCompletable.toTypedArray()) .thenApply { _: Void? -> citiesCompletable .stream() .map { it.join() } .collect(toList()) } citiesCompletableFutureOfList }

我使用了一個名為CompletableFuture.allOf的運(yùn)算符,該運(yùn)算符返回“ Void”類型,并且必須強(qiáng)制返回所需的“” CompletableFuture <List <City >>類型。

使用Project Reactor

Project Reactor是Reactive Streams規(guī)范的實現(xiàn)。 它有兩種特殊類型,可返回0/1項目流和0 / n項目流–前者是Mono,后者是Flux。

Project Reactor提供了一組非常豐富的運(yùn)算符,這些運(yùn)算符允許以多種方式轉(zhuǎn)換數(shù)據(jù)流。 首先考慮該函數(shù)以返回城市ID列表:

private fun getCityIds(): Flux<Long> { return webClient.get() .uri( "/cityids" ) .exchange() .flatMapMany { response -> LOGGER.info( "Received cities.." ) response.bodyToFlux<Long>() } }

我正在使用Spring出色的WebClient庫進(jìn)行遠(yuǎn)程調(diào)用,并獲得Project反應(yīng)器“ Mono <ClientResponse>”類型的響應(yīng),可以使用“ flatMapMany”運(yùn)算符將其修改為“ Flux <Long>”類型。

在給定城市ID的情況下,沿著相同的路線獲取城市的詳細(xì)信息:

private fun getCityDetail(cityId: Long?): Mono<City> { return webClient.get() .uri( "/cities/{id}" , cityId!!) .exchange() .flatMap { response -> val city: Mono<City> = response.bodyToMono() LOGGER.info( "Received city.." ) city } }

在這里,使用“ flatMap”運(yùn)算符將項目反應(yīng)堆“ Mono <ClientResponse>”類型轉(zhuǎn)換為“ Mono <City>”類型。

以及從中獲取城市ID和城市的代碼:

val cityIdsFlux: Flux<Long> = getCityIds() val citiesFlux: Flux<City> = cityIdsFlux .flatMap { this .getCityDetail(it) } return citiesFlux

這非常具有表現(xiàn)力-對比了基于回調(diào)的方法的混亂和基于響應(yīng)流的方法的簡單性。

結(jié)論

在我看來,這是使用基于響應(yīng)流的方法的最大原因之一,尤其是在涉及跨越異步邊界的場景(例如在這種情況下進(jìn)行遠(yuǎn)程調(diào)用)的情況下,尤其是Project Reactor。 它清除了各種回調(diào)和回調(diào)地獄,并提供了使用一組豐富的運(yùn)算符修改/轉(zhuǎn)換類型的自然方法。

我在這里使用的所有示例的工作版本的存儲庫位于https://github.com/bijukunjummen/reactive-cities-demo/tree/master/src/test/kotlin/samples/geo/kotlin

翻譯自: https://www.javacodegeeks.com/2019/06/callback-hell-reactive-patterns.html

總結(jié)

以上是生活随笔為你收集整理的回调地狱和反应模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。