private function writeSequential()

in src/Downloader.php [253:330]


    private function writeSequential(array &$context): GuzzleHttp\Promise\Promise
    {
        return GuzzleHttp\Promise\Coroutine::of(function () use (&$context) {
            // get object meta
            $request = new Models\HeadObjectRequest();
            Utils::copyRequest($request, $context['request']);
            yield $this->client->headObjectAsync($request)->then(
                function (Models\HeadObjectResult $result) use (&$context) {
                    $pos = $context['epos'];
                    $context['epos'] = $pos < 0 ? $result->contentLength : min($pos, $result->contentLength);
                },
            );

            // download parallel & write sequential
            $context['errors'] = [];
            $context['parts'] = [];
            $context['wstart'] = $context['pos'];

            $downloadFns = function () use (&$context) {
                foreach (self::iterPart($context) as $args) {
                    /**
                     * @var Models\GetObjectRequest $request
                     */
                    $request = clone $context['request'];
                    $request->rangeHeader = sprintf('bytes=%d-%d', $args[0], $args[0] + $args[1] - 1);
                    $request->rangeBehavior = 'standard';
                    $request->progressFn = null;
                    yield $this->client->getObjectAsync($request)->otherwise(
                        function ($reason) use (&$context) {
                            $context['errors'][] = $reason;
                            return GuzzleHttp\Promise\Create::rejectionFor($reason);
                        },
                    );
                    if (!empty($context['errors'])) {
                        break;
                    }
                }
            };

            $each = new GuzzleHttp\Promise\EachPromise(
                $downloadFns(),
                [
                    'concurrency' => $context['parallel_num'],
                    'fulfilled' => function (Models\GetObjectResult $result, $key) use (&$context) {
                        $rangeStart = $result->contentRange != null ? Utils::parseContentRange($result->contentRange) : 0;
                        $rangeStart = \is_array($rangeStart) ? $rangeStart[0] : 0;
                        $context['parts'][] = ['start' => $rangeStart, 'result' => $result];
                        self::drainPart($context);
                        return $result;
                    }
                ]
            );
            yield $each->promise();

            // remains parts
            self::drainPart($context);

            if (!empty($context['errors'])) {
                throw end($context['errors']);
            }

            if (!empty($context['parts'])) {
                throw new \RuntimeException('There are still unsaved parts.');
            }
        })->then(
            function ($result) use (&$context) {
                $res = new Models\DownloadResult();
                $res->written = $context['epos'] - $context['pos'];
                return $res;
            },
            function ($reason) use (&$context) {
                return GuzzleHttp\Promise\Create::rejectionFor(new Exception\DownloadException(
                    $context['filepath'] ?? '',
                    $reason
                ));
            }
        );
    }