queueMessageForProcessing({accountId, folderId, imapMessage, struct, desiredParts, throttle = true} = {}) {
    return new Promise(async (resolve) => {
      let logger;
      let folder;
      try {
        const accountDb = await LocalDatabaseConnector.forShared()
        const account = await accountDb.Account.findById(accountId)
        const db = await LocalDatabaseConnector.forAccount(accountId);
        const {Folder} = db
        folder = await Folder.findById(folderId)
        logger = global.Logger.forAccount(account)

        this._queueLength++
        this._queue = this._queue.then(async () => {
          if (this._currentChunkSize === 0) {
            this._currentChunkStart = Date.now();
          }
          this._currentChunkSize++;

          await this._processMessage({db, accountId, folder, imapMessage, struct, desiredParts, logger})
          this._queueLength--

          // Throttle message processing to meter cpu usage
          if (this._currentChunkSize === MAX_CHUNK_SIZE) {
            if (throttle) {
              await new Promise(r => setTimeout(r, this._computeThrottlingTimeout()));
            }
            this._currentChunkSize = 0;
          }

          // To save memory, we reset the Promise chain if the queue reaches a
          // length of 0, otherwise we will continue referencing the entire chain
          // of promises that came before
          if (this._queueLength === 0) {
            this._queue = Promise.resolve()
          }
          resolve();
        });
      } catch (err) {
        if (logger && folder) {
          await this._onError({imapMessage, desiredParts, folder, err, logger});
        } else {
          NylasEnv.reportError(err);
        }
        resolve();
      }
    })
  }
@returns Promise that resolves when message has been processed. This
promise will never reject. If message processing fails, we will register
the failure in the folder syncState.