docs: function processBatch(batch, cancellable, context) {
    const task = this;
  
    // Start execution of every one of the process one tasks on each and every one of the batch's messages
    const executeAllProcessOneTasksTask = task.getSubTask(executeAllProcessOneTasks.name);
    const p1 = executeAllProcessOneTasksTask.execute(batch, cancellable, context);
    const processOneTasksPromise = whenDone(executeAllProcessOneTasksTask, p1, cancellable, context);
  
    // Start execution of every one of the process all tasks on the batch
    const executeAllProcessAllTasksTask = task.getSubTask(executeAllProcessAllTasks.name);
    const p2 = executeAllProcessAllTasksTask.execute(batch, cancellable, context);
    const processAllTasksPromise = whenDone(executeAllProcessAllTasksTask, p2, cancellable, context);
  
    // Discard any and all unusable records
    const discardAnyUnusableRecordsTask = task.getSubTask(discardUnusableRecords.name);
    const p3 = discardAnyUnusableRecordsTask.execute(batch, cancellable, context);
    const discardUnusableRecordsPromise = whenDone(discardAnyUnusableRecordsTask, p3, cancellable, context);
  
    const preFinaliseBatchTask = task.getSubTask(preFinaliseBatch.name);
  
    // Create a processed promise that will only complete when the process one, process all and discardUnusableRecords promises complete
    const processedPromise = Promises.every([processOneTasksPromise, processAllTasksPromise, discardUnusableRecordsPromise],
      cancellable, context);
  
    // Set a timeout to trigger when a configurable percentage of the remaining time in millis is reached, which will give
    // us a bit of time to finalise at least some of the message processing before we run out of time to complete
    // everything in this invocation
    const timeoutCancellable = {};
    const timeoutMs = calculateTimeoutMs(context.streamProcessing.timeoutAtPercentageOfRemainingTime, context);
  
    context.debug(`Creating a process batch timeout to trigger after ${timeoutMs} ms (remaining time: ${context.awsContext.getRemainingTimeInMillis()} ms)`);
  
    const timeoutPromise = createTimeoutPromise(task, timeoutMs, timeoutCancellable, cancellable, context)
      .then(timeoutTriggered => {
        if (timeoutTriggered) { // If the timeout triggered then
          // timeout any and all of the process one and all tasks on the messages (reusing the timeout error set on the processing task by createTimeoutPromise if any)
          const timeoutError = isInstanceOf(task.error, TimeoutError) ? task.error :
            new TimeoutError(`Ran out of time to complete ${task.name}`);
          batch.timeoutProcessingTasks(timeoutError);
          context.warn(timeoutError);
          return new Failure(timeoutError);
        }
        return timeoutTriggered;
      });
    ignoreUnhandledRejection(timeoutPromise);
  
    // Build a completed promise that will only complete when the processed promise completes
    const completedPromise = createCompletedPromise(task, processedPromise, batch, timeoutCancellable, context);
    ignoreUnhandledRejection(completedPromise);
  
    // Set up a race between the completed promise and the timeout promise
    return Promise.race([completedPromise, timeoutPromise]).then(
      result => {
        // Pre-finalise the batch after successful processing
        const p = preFinaliseBatchTask.execute(batch, context);
        return whenDone(preFinaliseBatchTask, p, cancellable, context)
          .then(() => result, () => result);
      },
      err => {
        // Pre-finalise the batch after failed processing
        const p = preFinaliseBatchTask.execute(batch, context);
        return whenDone(preFinaliseBatchTask, p, cancellable, context)
          .then(() => Promise.reject(err), () => Promise.reject(err));
      });
  }