class DownloaderController() { ... suspend fun download(downloadRequest: DownloadRequest.Download): Flow = mutex.withLock { withContext(Dispatchers.IO) { if (channelMap.containsKey(downloadRequest.url)) { return@withContext channelMap[downloadRequest.url]!!.asFlow() } val downloadChannel = ConflatedBroadcastChannel() channelMap[downloadRequest.url] = downloadChannel jobMap[downloadRequest.url] = launch { startDownloadSendChannel(downloadRequest, downloadChannel) } //It does not return immediately. It returns when launch coroutine complete to collect all items from flow. return@withContext channelMap[downloadRequest.url]!!.asFlow() } } private suspend fun startDownloadSendChannel(downloadRequest: DownloadRequest.Download,channel: ConflatedBroadcastChannel) { downloader.download(downloadRequest) .buffer() .flowOn(Dispatchers.IO) .collect { channel.send(it) } } } class ViewModel { ... viewModelScope.launch { downloaderController .download(requestList.first()) .collect { //Waits for downloaderController.download method and all of its child coroutines complete //Not start colllecting immediately. } }