Search code examples
androidkotlinkotlin-coroutinescoroutinecoroutinescope

How to implement Promise.all like functionality in kotlin


Am trying to get messages from twillio server using its sdk method so on calling the method it return callback to return the List of messages. I have list of conversation,i want to get all messages for conversation so am using forEach like this

  allConversations.forEach { conversation ->
            conversation.getMessageByIndex(conversation.lastMessageIndex){
                conversationLastMessages[conversation.sid] = it
            }
        }

So i want to wait until all listners get executed and then want to change the state of ui.


Solution

  • You can make all requests in parallel and wait for all of them to finish following next steps:

    1. Create a suspend function getMessage, which will be responsible for suspending the calling coroutine until request is executed. You can use suspendCoroutine or suspendCancellableCoroutine for that:

      suspend fun getMessage(conversation: Conversation) = suspendCoroutine<Message?> { continuation ->
          conversation.getMessageByIndex(conversation.lastMessageIndex, object : CallbackListener<Message> {
              override fun onError(errorInfo: ErrorInfo) {
                  continuation.resume(null) // resume calling coroutine
                  // or continuation.resumeWithException() depend whether you want to handle Exception or not
              }
      
              override fun onSuccess(result: Message) {
                  continuation.resume(result) // resume calling coroutine
              }
          })
      }
      
    2. Run requests in parallel using async coroutine builder and Dispatchers.IO dispatcher to offload work from the Main Thread:

      async(Dispatchers.IO) {
          getMessage(conversation)
      }
      
    3. To run all this you need to use some instance of CoroutineScope to launch a coroutine. In ViewModel it can be viewModelScope, in Activity/Fragment - lifecycleScope. For example in ViewModel:

      viewModelScope.launch {
          val allConversations = ...
      
          allConversations.map { conversation ->
              async(Dispatchers.IO) {
                  getMessage(conversation)
              }
          }.awaitAll() // waiting for all request to finish executing in parallel
           .forEach { message ->   // iterate over List<Message> and fill conversationLastMessages
               conversationLastMessages[message.getConversationSid()] = message
           }
      
           // here all requests are completed and UI can be updated
      }