function iter(i) {
  NativeCollection.prototype[i] = function() {
    const collection = this._getCollection();
    const args = Array.from(arguments);
    const _this = this;
    const globalDebug = _this &&
      _this.conn &&
      _this.conn.base &&
      _this.conn.base.options &&
      _this.conn.base.options.debug;
    const connectionDebug = _this &&
      _this.conn &&
      _this.conn.options &&
      _this.conn.options.debug;
    const debug = connectionDebug == null ? globalDebug : connectionDebug;
    const lastArg = arguments[arguments.length - 1];
    const opId = new ObjectId();

    // If user force closed, queueing will hang forever. See #5664
    if (this.conn.$wasForceClosed) {
      const error = new MongooseError('Connection was force closed');
      if (args.length > 0 &&
        typeof args[args.length - 1] === 'function') {
        args[args.length - 1](error);
        return;
      } else {
        throw error;
      }
    }

    let _args = args;
    let callback = null;
    if (this._shouldBufferCommands() && this.buffer) {
      this.conn.emit('buffer', {
        _id: opId,
        modelName: _this.modelName,
        collectionName: _this.name,
        method: i,
        args: args
      });

      let callback;
      let _args = args;
      let promise = null;
      let timeout = null;
      if (syncCollectionMethods[i] && typeof lastArg === 'function') {
        this.addQueue(i, _args);
        callback = lastArg;
      } else if (syncCollectionMethods[i]) {
        promise = new this.Promise((resolve, reject) => {
          callback = function collectionOperationCallback(err, res) {
            if (timeout != null) {
              clearTimeout(timeout);
            }
            if (err != null) {
              return reject(err);
            }
            resolve(res);
          };
          _args = args.concat([callback]);
          this.addQueue(i, _args);
        });
      } else if (typeof lastArg === 'function') {
        callback = function collectionOperationCallback() {
          if (timeout != null) {
            clearTimeout(timeout);
          }
          return lastArg.apply(this, arguments);
        };
        _args = args.slice(0, args.length - 1).concat([callback]);
      } else {
        promise = new Promise((resolve, reject) => {
          callback = function collectionOperationCallback(err, res) {
            if (timeout != null) {
              clearTimeout(timeout);
            }
            if (err != null) {
              return reject(err);
            }
            resolve(res);
          };
          _args = args.concat([callback]);
          this.addQueue(i, _args);
        });
      }

      const bufferTimeoutMS = this._getBufferTimeoutMS();
      timeout = setTimeout(() => {
        const removed = this.removeQueue(i, _args);
        if (removed) {
          const message = 'Operation `' + this.name + '.' + i + '()` buffering timed out after ' +
            bufferTimeoutMS + 'ms';
          const err = new MongooseError(message);
          this.conn.emit('buffer-end', { _id: opId, modelName: _this.modelName, collectionName: _this.name, method: i, error: err });
          callback(err);
        }
      }, bufferTimeoutMS);

      if (!syncCollectionMethods[i] && typeof lastArg === 'function') {
        this.addQueue(i, _args);
        return;
      }

      return promise;
    } else if (!syncCollectionMethods[i] && typeof lastArg === 'function') {
      callback = function collectionOperationCallback(err, res) {
        if (err != null) {
          _this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: _this.name, method: i, error: err });
        } else {
          _this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: _this.name, method: i, result: res });
        }
        return lastArg.apply(this, arguments);
      };
      _args = args.slice(0, args.length - 1).concat([callback]);
    }

    if (debug) {
      if (typeof debug === 'function') {
        let argsToAdd = null;
        if (typeof args[args.length - 1] == 'function') {
          argsToAdd = args.slice(0, args.length - 1);
        } else {
          argsToAdd = args;
        }
        debug.apply(_this,
          [_this.name, i].concat(argsToAdd));
      } else if (debug instanceof stream.Writable) {
        this.$printToStream(_this.name, i, args, debug);
      } else {
        const color = debug.color == null ? true : debug.color;
        const shell = debug.shell == null ? false : debug.shell;
        this.$print(_this.name, i, args, color, shell);
      }
    }

    this.conn.emit('operation-start', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, params: _args });

    try {
      if (collection == null) {
        const message = 'Cannot call `' + this.name + '.' + i + '()` before initial connection ' +
          'is complete if `bufferCommands = false`. Make sure you `await mongoose.connect()` if ' +
          'you have `bufferCommands = false`.';
        throw new MongooseError(message);
      }

      if (syncCollectionMethods[i] && typeof lastArg === 'function') {
        const result = collection[i].apply(collection, _args.slice(0, _args.length - 1));
        this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, result });
        return lastArg.call(this, null, result);
      }

      const ret = collection[i].apply(collection, _args);
      if (ret != null && typeof ret.then === 'function') {
        return ret.then(
          result => {
            if (typeof lastArg === 'function') {
              lastArg(null, result);
            } else {
              this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, result });
            }
            return result;
          },
          error => {
            if (typeof lastArg === 'function') {
              lastArg(error);
              return;
            } else {
              this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, error });
            }
            throw error;
          }
        );
      }
      return ret;
    } catch (error) {
      // Collection operation may throw because of max bson size, catch it here
      // See gh-3906
      if (typeof lastArg === 'function') {
        return lastArg(error);
      } else {
        this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, error: error });

        throw error;
      }
    }
  };
}
Copy the collection methods and make them subject to queues
@param {Number|String} I
@api private