【问题标题】:API returns an array that I need to resolve; one request per itemAPI 返回一个我需要解析的数组;每个项目一个请求
【发布时间】:2021-07-27 16:42:44
【问题描述】:

我有从后端返回的重复数据结构(可观察),它看起来像:

[{
     id:1,
     userId: 111,
     name: '',
     children :[
      { 
        id:3,
        userId: 333,
        name: '',
        children: [...]
      }
     ]
    },
    {
     id:2,
     userId:111,
     name:'',
     children: [...]
   }]

我有另一个通过用户 ID 返回用户名的端点。我需要使用每个 ID 调用此服务并将返回的名称映射到结构。有没有什么漂亮的解决方案可以使用 RxJs 操作符来实现这一点?

【问题讨论】:

    标签: rxjs observable


    【解决方案1】:

    您可以尝试以下方法。有关详细信息,请参阅内联 cmets。

    // fetchStruct is a function that returns an Observable which notifies the initial structure
    const struct$ = fetchStruct();
    // here we start the RxJs pipe transformation
    struct$.pipe(
      // when struct$ emits the initial structure we pass the control to another observable chain
      // this is done via the concatMap operator
      concatMap(beStruct => {  // beStruct is the structure returned by the back end
        // from beStruct we construct an array of Observables
        // fetchName is a function that returns an Observable that emits when the name is returned
        arrObs = beStruct.map(el => fetchName(el.id))
        // with forkJoin we execute all the Observables in the array in parallel
        forkJoin(arrObs).pipe(
          // forkJoin emits when all Observables have notified and it will emit
          // an array of values with the same order as arrObs
          // we can therefore loop through this array to enrich beStruct with the names
          map(names => {
            names.forEach((n, i) => beStruct[i].name = n);
            return beStruct;
          })
        )
      })
    )
    

    这是一个非常典型的 RxJs 案例。您可能会在this blog 中找到其他一些常见模式。

    【讨论】:

      【解决方案2】:

      首先,我假设.children 可以是 id 数组,即指向其他用户的指针。 (请参见下面的示例。)

      我要做的是将初始的users 数组转换为可观察的数组。每个人都会请求将 id 映射到名称。使用merge(…) 触发请求。

      当然,您不希望同时进行一百万个 HTTP 请求,因此您可以将 merge 的并发参数设置为适合您的数字。 (在我的示例中,我将其设置为 2。)

      既然你有一个数组,你想要一个数组输出。您可以使用 toArray 运算符简单地将其累积到一个数组中。

      在下面的例子中:

      1. 我将如何处理初始 users 数组(即 .children 应该是指针)
      2. users 映射到可观察对象数组中以解析名称
      3. 合并该 observables 数组并将并发设置为 2
      4. 将结果累加到一个数组中

      const get_name = user => fake_backend[user.id].pipe(map(name => ({...user, name})));
      //                       ^^^^^^^^^^^^^^^^^^^^^                  ^^^^^^^^^^^^^^^^^
      //                       A                                      B
      //
      // A: Simulate HTTP request latency to lookup an id and return a name
      // B: Merge name into initial user object
      
      merge(...users.map(get_name), 2).pipe(toArray()).subscribe(final_users => {
      //                            ^       ^^^^^^^^^
      //                            A       B
      //
      // A: Concurrency. Maximum two HTTP requests at any one time.
      // B: Accumulate each output into an array. Complete when merge is done.
        console.log(final_users);
      });
      <script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.2.0/rxjs.umd.min.js" integrity="sha512-MlqMFvHwgWJ1vfts5fdC2WzxDaIXWfYuAd9Tb2lobtF61Gk+HIRDrbtxgasBSM9lZgOK9ilwK9LqFIYEV+k0IA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
      <script>
      const {merge, of} = rxjs;
      const {tap, delay, map, toArray} = rxjs.operators;
      
      const users =
        [ { id:       1
          , userId:   111
          , name:     ''
          , children: [3]}
      
        , { id:       2   
          , userId:   111 
          , name:     ''  
          , children: [4]} 
      
        , { id:       3
          , userId:   333
          , name:     ''
          , children: []}
      
        , { id:       4
          , userId:   444
          , name:     ''
          , children: []}];
      
      const fake_backend =
        { 1: of('Foo').pipe(delay(3000), tap(() => console.log('user 1 ✓')))
        , 2: of('Bar').pipe(delay(2000), tap(() => console.log('user 2 ✓')))
        , 3: of('Baz').pipe(delay(1000), tap(() => console.log('user 3 ✓')))
        , 4: of('Bat').pipe(delay(1000), tap(() => console.log('user 4 ✓'))) };
      </script>

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-06-28
        • 2011-08-23
        • 1970-01-01
        • 1970-01-01
        • 2013-02-19
        • 2018-03-06
        • 2013-09-19
        • 1970-01-01
        相关资源
        最近更新 更多