jhiertz
05/21/2024, 2:41 PMprivate var mutex = Mutex()
private val results = mutableListOf<Any>()
private suspend fun addResultsToList(data: List<Any>) {
mutex.withLock { results.addAll(data) }
}
fun search(query: String, searchCriteria: SearchCriteria, delayMs: Long = 500L) {
searchJob?.cancel()
searchJob = searchScope.launch {
delay(delayMs)
val apis = listOf(
async { addResultsToList(api1(query, searchCriteria)) },
async { addResultsToList(api2(query, searchCriteria)) },
async { addResultsToList(api3(query, searchCriteria)) },
async { addResultsToList(api4(query, searchCriteria)) }
)
apis.awaitAll()
}
}
Stanislav Kral
05/21/2024, 2:50 PMapi1
call before api2-4
results are available?Sam
05/21/2024, 2:58 PMresults
list, and when?reline
05/21/2024, 6:11 PMchannelFlow
+ debounce
private val delay = 500.milliseconds
private val search = Channel<Search>()
fun search(query: String, searchCriteria: SearchCriteria) {
search.trySendBlocking(Search(query, searchCriteria))
}
val results: Flow<List<Any>> = search.receiveAsFlow()
.debounce(delay)
.flatMapLatest { (query, searchCriteria) ->
api(query, searchCriteria)
}
private fun api(query: String, searchCriteria: SearchCriteria): Flow<List<Any>> {
return channelFlow {
launch { send(api1(query, searchCriteria)) }
launch { send(api2(query, searchCriteria)) }
launch { send(api3(query, searchCriteria)) }
launch { send(api4(query, searchCriteria)) }
}.runningFold(emptyList()) { acc, value -> acc + value }
}
reline
05/21/2024, 6:13 PMjhiertz
05/22/2024, 7:16 AMStanislav Kral
05/22/2024, 7:23 AMresults
list from multiple places, e.g., from several screens?
If not, you can delete val results
and your api
method should return a Flow
emitting intermediate search results.jhiertz
05/22/2024, 7:25 AMStanislav Kral
05/22/2024, 8:31 AMclass SearchCriteria()
fun api1(query: String, searchCriteria: SearchCriteria) = listOf("foo", "bar")
fun api2(query: String, searchCriteria: SearchCriteria) = listOf("baz", "1")
fun api3(query: String, searchCriteria: SearchCriteria) = listOf("2", "3")
private fun search(query: String, searchCriteria: SearchCriteria): Flow<List<String>> {
return channelFlow {
launch { send(api1(query, searchCriteria)) }
launch { send(api2(query, searchCriteria)) }
launch { send(api3(query, searchCriteria)) }
}.runningFold(emptyList()) { acc, value -> acc + value }
}
when the flow completes, it indicates that all search results have been collectedStanislav Kral
05/22/2024, 8:31 AM@Test
fun searchTest() = runTest {
val expectedApiSearchResults = listOf(
listOf("foo", "bar"),
listOf("baz", "1"),
listOf("2", "3")
)
val expectedFinalSearchResult = expectedApiSearchResults.flatten()
search(query = "testquery", searchCriteria = SearchCriteria()).test {
// collect intermediate search results
val intermediateSearchResults = mutableListOf<List<String>>()
// verify there are exactly 4 intermediate search results (including an empty list)
repeat(expectedApiSearchResults.size + 1) { i ->
// await the next intermediate search result
val intermediateSearchResult = awaitItem()
// verify that the intermediate search results are subsets of the final search result
assertTrue(
intermediateSearchResult.all {
it in expectedFinalSearchResult
}
)
intermediateSearchResults.add(intermediateSearchResult)
if (i > 0) {
// verify that individual API results are emitted as part of the intermediate search results
val lastDiffBetweenIntermediateSearchResults = intermediateSearchResults[i] - intermediateSearchResults[i - 1]
assertTrue(lastDiffBetweenIntermediateSearchResults in expectedApiSearchResults)
}
}
// verify the final search result
assertEquals(expectedFinalSearchResult, intermediateSearchResults.last())
awaitComplete()
}
}
jhiertz
05/22/2024, 9:13 AM