This loop is responsible for popping items off the send
        queue, encrypting them, and sending them over the network.

        Besides `connect`, only this method ever sends data.

async def _send_loop(self):
        """
        This loop is responsible for popping items off the send
        queue, encrypting them, and sending them over the network.

        Besides `connect`, only this method ever sends data.
        """
        while self._user_connected and not self._reconnecting:
            if self._pending_ack:
                ack = RequestState(MsgsAck(list(self._pending_ack)), self._loop)
                self._send_queue.append(ack)
                self._last_acks.append(ack)
                self._pending_ack.clear()

            self._log.debug('Waiting for messages to send...')
            # TODO Wait for the connection send queue to be empty?
            # This means that while it's not empty we can wait for
            # more messages to be added to the send queue.
            batch, data = await self._send_queue.get()

            if not data:
                continue

            self._log.debug('Encrypting %d message(s) in %d bytes for sending',
                          len(batch), len(data))

            data = self._state.encrypt_message_data(data)
            try:
                await self._connection.send(data)
            except IOError as e:
                self._log.info('Connection closed while sending data')
                self._start_reconnect(e)
                return

            for state in batch:
                if not isinstance(state, list):
                    if isinstance(state.request, TLRequest):
                        self._pending_state[state.msg_id] = state
                else:
                    for s in state:
                        if isinstance(s.request, TLRequest):
                            self._pending_state[s.msg_id] = s

            self._log.debug('Encrypted messages put in a queue to be sent')