private void FetchNextPage()
            {
                collectionSource.GetPageAsync(fetchedDocuments, PageSize, null).ContinueOnUIThread(t =>
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        tcs.SetCanceled();
                        CleanUp();
                        return;
                    }

                    if (!t.IsFaulted)
                    {
                        WriteColumnsForDocuments(writer, t.Result, extractor);
                        fetchedDocuments += t.Result.Count;

                        ReportProgress((int)(((double)fetchedDocuments / collectionSource.Count) * 100));

                        if (fetchedDocuments < collectionSource.Count)
                        {
                            FetchNextPage();
                        }
                        else
                        {
                            tcs.SetResult(true);
                            CleanUp();
                        }
                    }
                    else
                    {
                        tcs.SetException(t.Exception);
                        CleanUp();
                    }
                });
            }
        private void ProcessPageRequests()
        {
            while (_inProcessPageRequests < MaxConcurrentPageRequests && _pendingPageRequests.Count > 0)
            {
                var request = _pendingPageRequests.Pop();

                // if we encounter a requested posted for an early collection state,
                // we can ignore it, and all that came before it
                if (State != request.StateWhenRequested)
                {
                    _pendingPageRequests.Clear();
                    break;
                }

                // check that the page is still requested (the user might have scrolled, causing the
                // page to be ejected from the cache
                if (!_requestedPages.Contains(request.Page))
                {
                    break;
                }

                _inProcessPageRequests++;

                var tsk = _source.GetPageAsync(request.Page * _pageSize, _pageSize, _sortDescriptions);
                tsk.ContinueWith(
                    t =>
                {
                    if (!t.IsFaulted)
                    {
                        UpdatePage(request.Page, t.Result, request.StateWhenRequested, request.PreviousNextRequest);
                    }
                    else
                    {
                        MarkPageAsError(request.Page, request.StateWhenRequested);
                    }

                    // fire off any further requests
                    _inProcessPageRequests--;
                    ProcessPageRequests();
                }, _synchronizationContextScheduler);

                tsk.Start();
            }
        }