/**
  * Flushes the batch by combining all the queued put and delete requests
  * into BatchWriteItem commands and executing them. Unprocessed items are
  * automatically re-queued.
  *
  * @param bool $untilEmpty If true, flushing will continue until the queue
  *                         is completely empty. This will make sure that
  *                         unprocessed items are all eventually sent.
  *
  * @return $this
  */
 public function flush($untilEmpty = true)
 {
     // Send BatchWriteItem requests until the queue is empty
     $keepFlushing = true;
     while ($this->queue && $keepFlushing) {
         $commands = $this->prepareCommands();
         $pool = new CommandPool($this->client, $commands, ['before' => $this->config['before'], 'concurrency' => $this->config['pool_size'], 'fulfilled' => function (ResultInterface $result) {
             // Re-queue any unprocessed items
             if ($result->hasKey('UnprocessedItems')) {
                 $this->retryUnprocessed($result['UnprocessedItems']);
             }
         }, 'rejected' => function ($reason) {
             if ($reason instanceof AwsException) {
                 $code = $reason->getAwsErrorCode();
                 if ($code === 'ProvisionedThroughputExceededException') {
                     $this->retryUnprocessed($reason->getCommand()['RequestItems']);
                 } elseif (is_callable($this->config['error'])) {
                     $this->config['error']($reason);
                 }
             }
         }]);
         $pool->promise()->wait();
         $keepFlushing = (bool) $untilEmpty;
     }
     return $this;
 }
 /**
  * Upload the source asynchronously using multipart upload operations.
  *
  * @return PromiseInterface
  */
 public function promise()
 {
     if ($this->promise) {
         return $this->promise;
     }
     return $this->promise = Promise\coroutine(function () {
         // Initiate the upload.
         if ($this->state->isCompleted()) {
             throw new \LogicException('This multipart upload has already ' . 'been completed or aborted.');
         } elseif (!$this->state->isInitiated()) {
             $result = (yield $this->execCommand('initiate', $this->getInitiateParams()));
             $this->state->setUploadId($this->info['id']['upload_id'], $result[$this->info['id']['upload_id']]);
             $this->state->setStatus(UploadState::INITIATED);
         }
         // Create a command pool from a generator that yields UploadPart
         // commands for each upload part.
         $resultHandler = $this->getResultHandler($errors);
         $commands = new CommandPool($this->client, $this->getUploadCommands($resultHandler), ['concurrency' => $this->config['concurrency'], 'before' => $this->config['before_upload']]);
         // Execute the pool of commands concurrently, and process errors.
         (yield $commands->promise());
         if ($errors) {
             throw new MultipartUploadException($this->state, $errors);
         }
         // Complete the multipart upload.
         (yield $this->execCommand('complete', $this->getCompleteParams()));
         $this->state->setStatus(UploadState::COMPLETED);
     })->otherwise(function (\Exception $e) {
         // Throw errors from the operations as a specific Multipart error.
         if ($e instanceof AwsException) {
             $e = new MultipartUploadException($this->state, $e);
         }
         throw $e;
     });
 }