docs: def pub_async(self,
                    tgt,
                    fun,
                    arg=(),
                    tgt_type='glob',
                    ret='',
                    jid='',
                    timeout=5,
                    io_loop=None,
                    listen=True,
                    **kwargs):
          '''
          Take the required arguments and publish the given command.
          Arguments:
              tgt:
                  The tgt is a regex or a glob used to match up the ids on
                  the minions. Salt works by always publishing every command
                  to all of the minions and then the minions determine if
                  the command is for them based on the tgt value.
              fun:
                  The function name to be called on the remote host(s), this
                  must be a string in the format "<modulename>.<function name>"
              arg:
                  The arg option needs to be a tuple of arguments to pass
                  to the calling function, if left blank
          Returns:
              jid:
                  A string, as returned by the publisher, which is the job
                  id, this will inform the client where to get the job results
              minions:
                  A set, the targets that the tgt passed should match.
          '''
          # Make sure the publisher is running by checking the unix socket
          if (self.opts.get('ipc_mode', '') != 'tcp' and
                  not os.path.exists(os.path.join(self.opts['sock_dir'],
                  'publish_pull.ipc'))):
              log.error(
                  'Unable to connect to the salt master publisher at %s',
                  self.opts['sock_dir']
              )
              raise SaltClientError
  
          payload_kwargs = self._prep_pub(
                  tgt,
                  fun,
                  arg,
                  tgt_type,
                  ret,
                  jid,
                  timeout,
                  **kwargs)
  
          master_uri = 'tcp://' + salt.utils.zeromq.ip_bracket(self.opts['interface']) + \
                       ':' + six.text_type(self.opts['ret_port'])
          channel = salt.transport.client.AsyncReqChannel.factory(self.opts,
                                                                  io_loop=io_loop,
                                                                  crypt='clear',
                                                                  master_uri=master_uri)
  
          try:
              # Ensure that the event subscriber is connected.
              # If not, we won't get a response, so error out
              if listen and not self.event.connect_pub(timeout=timeout):
                  raise SaltReqTimeoutError()
              payload = yield channel.send(payload_kwargs, timeout=timeout)
          except SaltReqTimeoutError:
              raise SaltReqTimeoutError(
                  'Salt request timed out. The master is not responding. You '
                  'may need to run your command with `--async` in order to '
                  'bypass the congested event bus. With `--async`, the CLI tool '
                  'will print the job id (jid) and exit immediately without '
                  'listening for responses. You can then use '
                  '`salt-run jobs.lookup_jid` to look up the results of the job '
                  'in the job cache later.'
              )
  
          if not payload:
              # The master key could have changed out from under us! Regen
              # and try again if the key has changed
              key = self.__read_master_key()
              if key == self.key:
                  raise tornado.gen.Return(payload)
              self.key = key
              payload_kwargs['key'] = self.key
              payload = yield channel.send(payload_kwargs)
  
          error = payload.pop('error', None)
          if error is not None:
              if isinstance(error, dict):
                  err_name = error.get('name', '')
                  err_msg = error.get('message', '')
                  if err_name == 'AuthenticationError':
                      raise AuthenticationError(err_msg)
                  elif err_name == 'AuthorizationError':
                      raise AuthorizationError(err_msg)
  
              raise PublishError(error)
  
          if not payload:
              raise tornado.gen.Return(payload)
  
          # We have the payload, let's get rid of the channel fast(GC'ed faster)
          channel.close()
  
          raise tornado.gen.Return({'jid': payload['load']['jid'],
                                    'minions': payload['load']['minions']})