简体   繁体   English

FP-TS:映射响应

[英]FP-TS: mapping responses

I am using the fp-ts library and I cannot figure out how to implement the following scenario:我正在使用fp-ts库,但我无法弄清楚如何实现以下场景:

  1. Let's say I have a service with the request method getBooks(shelf, page) and the response looks like this (the request is paginated):假设我有一个带有请求方法getBooks(shelf, page)的服务,响应看起来像这样(请求是分页的):
{ 
    totalItems: 100,  
    perPage: 25,  
    books:[{...}, ...],  
    ....
}
  1. So I would like to send an initial request and then calculate the number of pages:所以我想发送一个初始请求,然后计算页数:
const nrOfPages = Math.ceil(totalItems / perPage);
  1. And then loop to get the rest of the books as the first request will only provide me the first 25 book items.然后循环获取书籍的 rest,因为第一个请求只会为我提供前 25 本书。

Now the struggle is that in the end I would like to collect all the books inside one object.现在的斗争是,最后我想收集一本object里面的所有书。 Basically, I want to await the results and flatmap them together.基本上,我想等待结果并将它们放在一起。 It is also important that the requests should be sequential and use the fp-ts library.请求应该是顺序的并使用 fp-ts 库也很重要。

const allBooks [{...},{...},{...}, ...];

You can use traverseSeqArray from the Task module to map an array of page numbers into tasks to fetch each page, and each task will be executed sequentially.你可以使用traverseSeqArrayTask模块将 map 一个页码数组转换成任务来获取每个页面,并且每个任务会按顺序执行。 Then, you can useconcatAll (from Monoid ) to concatenate the arrays of books.然后,您可以使用concatAll (来自Monoid )连接书籍的 arrays。

declare const traverseSeqArray: <A, B>(f: (a: A) => Task<B>) => (as: readonly A[]) => Task<readonly B[]>
declare const concatAll: <A>(M: Monoid<A>) => (as: readonly A[]) => A
import * as M from 'fp-ts/lib/Monoid';
import * as RA from 'fp-ts/lib/ReadonlyArray';
import * as T from 'fp-ts/lib/Task';
import {flow, pipe} from 'fp-ts/lib/function';

declare const getBooks: (
    shelf: Shelf,
    page: number
) => T.Task<{totalItems: number; perPage: number; books: readonly Book[]}>;

const getAllBooks = (shelf: Shelf): T.Task<readonly Book[]> =>
    pipe(
        // Fetch the first page (assuming pages are zero-indexed)
        getBooks(shelf, 0),
        T.chain(({totalItems, perPage, books: firstPageBooks}) => {
            const nrOfPages = Math.ceil(totalItems / perPage);
            // e.g. [1, 2, 3] for 100 books and 25 per page
            const pagesToFetch = Array.from(
                {length: nrOfPages - 1},
                (_, i) => i + 1
            );
            return pipe(
                pagesToFetch,
                // With each page...
                T.traverseSeqArray(page =>
                    // ...fetch the books at the page
                    pipe(
                        getBooks(shelf, page),
                        T.map(({books}) => books)
                    )
                ),
                // Now we have a Task<Book[][]> that we want to turn into
                // a Task<Book[]> including the books from the first page
                T.map(
                    flow(
                        // Prepend the first pages’ books
                        RA.prepend(firstPageBooks),
                        // Concatenate the Book[][] into a Book[]
                        M.concatAll(RA.getMonoid())
                    )
                )
            );
        })
    );

This example assumes that getBooks does not fail, but tihs can easily be modified by switching out Task to TaskEither .此示例假设getBooks不会失败,但可以通过将Task切换到 TaskEither 轻松修改TaskEither

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM