如何在JavaScript中映射一个对象数组并为每个对象获取网络数据?

3

我将在React Native中制作应用程序。我需要从一个URL获取类别数据数组,然后对于每个类别,我需要从它们各自的URL获取资源。

我的数据以以下格式存储:

来自mainURL

{
  "id": 1,
  "name": "Home",
  "type": "",
  "url": "",
  "subCategories": [
    {
      "id": 92,
      "name": "Documentary",
      "type": "JTV_LEAF",
      "url": "yyy",
    }
  ]
}

从每个类别的URL中,

[
  {
    "id": "1",
    "title": "Inception",
    "type": "vod"
  }
]

我该如何使用mapreduceaxios获取每个类别的数据?
这是我目前写的代码。最后我得到了undefined
export const fetchNavigationFeed = (navUrl, subId) => {
  return dispatch => {
    const url = navUrl
      .replace("__JTV__SUBSCRIBER__ID__", subId);
    dispatch({ type:FETCH_NAVIGATION_FEED });
    return axios.get(url)
      .then(response => {
        let categories = [];
        for (var i = 0; i < response.data.subCategories.length; i++) {
            var cat = response.data.subCategories[i];
            var category = new Category(cat);
            categories.push(category);
        }
        console.log(categories);
        let promises = [];
        categories.map(category => {
          let request = axios.get(category.url)
            .then(assetsJson => {
              let assets = [];
              for (var i = 0; i < assetsJson.data.length; i++) {
                  var ass = assetsJson.data[i];
                  var asset = new Asset(ass);
                  assets.push(asset);
              }
              category.assets = assets;
            });
          promises.push(request);
        });
        axios.all(promises)
          .then(axios.spread(...args) => {
            console.log(args);
          });
        return categories;
      })
      .then(categories => {
        // console.log(categories);
        dispatch({ type:FETCH_NAVIGATION_FEED_SUCCESS, payload:categories });
      });
  }
}

你期望的输出是什么? - Ankit Agarwal
1
由于您使用了带有{}的箭头函数而非隐式返回,我希望您必须1)返回fetch链:return fetch(category.url); 以及2)分配完资产后将类别返回:.then((assets) => { category.assets = assets; return category });,以便提供给Promise.all的数组实际上包含这些类别的承诺。 - Shilly
@AnkitAgarwal 对于每个类别,都有包含资产列表的URL。我想要获取它们并将它们添加为每个类别的一个字段。 - khateeb
你已经编写的代码有什么问题,哪些地方没有按预期工作? - Bergi
@Bergi 结果并没有给出类别列表,而是一些 Promise 对象。 - khateeb
显示剩余2条评论
2个回答

4

以下是一个可工作的示例和jest测试:


code.js

import axios from 'axios';

export const FETCH_NAVIGATION_FEED = 'FETCH_NAVIGATION_FEED';
export const FETCH_NAVIGATION_FEED_SUCCESS = 'FETCH_NAVIGATION_FEED_SUCCESS';

class Category {
  constructor(json) {
    this.id = json.id;
    this.name = json.name;
    this.url = json.url;
  }
}

class Asset {
  constructor(json) {
    this.id = json.id;
    this.title = json.title;
  }
}

export const fetchNavigationFeed = (navUrl, subId) => {
  return async (dispatch) => {
    dispatch({ type: FETCH_NAVIGATION_FEED });

    const url = navUrl
      .replace('__JTV__SUBSCRIBER__ID__', subId);
    const response = await axios.get(url);
    const categories = [];
    const promises = [];
    response.data.subCategories.forEach((subCategory) => {
      const category = new Category(subCategory);
      categories.push(category);
      const promise = axios.get(category.url).then((subResponse) => {
        category.assets = [];
        subResponse.data.forEach((asset) => {
          category.assets.push(new Asset(asset));
        });
      });
      promises.push(promise);
    });
    // wait for all the promises simultaneously
    await Promise.all(promises);

    dispatch({ type: FETCH_NAVIGATION_FEED_SUCCESS, payload: categories });
  }
}

code.test.js

import axios from 'axios';
import {
  fetchNavigationFeed,
  FETCH_NAVIGATION_FEED,
  FETCH_NAVIGATION_FEED_SUCCESS
} from './code';

const getMock = jest.spyOn(axios, 'get');
getMock.mockImplementation((url) => {
  switch (url) {
    case 'mainUrl-mySubId':
      return Promise.resolve({
        data: {
          "id": 1,
          "name": "home",
          "subCategories": [
            {
              "id": 2,
              "name": "sub1",
              "url": "sub1Url",
            },
            {
              "id": 3,
              "name": "sub2",
              "url": "sub2Url",
            }
          ]
        }
      });
    case 'sub1Url':
      return Promise.resolve({
        data: [
          {
            "id": 4,
            "title": "asset1"
          },
          {
            "id": 5,
            "title": "asset2"
          }
        ]
      });
    case 'sub2Url':
      return Promise.resolve({
        data: [
          {
            "id": 6,
            "title": "asset3"
          },
          {
            "id": 7,
            "title": "asset4"
          }
        ]
      });
  }
});


test('getData', async () => {
  const asyncDispatch = fetchNavigationFeed('mainUrl-__JTV__SUBSCRIBER__ID__', 'mySubId');
  const dispatch = jest.fn();
  await asyncDispatch(dispatch);
  expect(dispatch).toHaveBeenCalledTimes(2);
  const firstCallArgs = dispatch.mock.calls[0];
  expect(firstCallArgs).toEqual([{
    type: FETCH_NAVIGATION_FEED
  }]);
  const secondCallArgs = dispatch.mock.calls[1];
  expect(secondCallArgs).toEqual([{
    type: FETCH_NAVIGATION_FEED_SUCCESS,
    payload: [
      {
        id: 2,
        name: 'sub1',
        url: 'sub1Url',
        assets: [
          {
            "id": 4,
            "title": "asset1"
          },
          {
            "id": 5,
            "title": "asset2"
          }
        ]
      },
      {
        id: 3,
        name: 'sub2',
        url: 'sub2Url',
        assets: [
          {
            "id": 6,
            "title": "asset3"
          },
          {
            "id": 7,
            "title": "asset4"
          }
        ]
      }
    ]
  }]);
});

注意:您可以使用 axios.all(),但根据此主题,它在底层使用Promise.all()


@khateeb,让我知道这是否覆盖了你所需求的,并且如果你对实现有任何问题,请随时提出。 - Brian Adams
我遇到了一个错误:await是一个保留关键字。我已经更新了我的示例代码。我应该在哪里放置async关键字?我正在使用react-nativereduxredux-thunk - khateeb
我明白了。非常感谢!我只是在一周前开始学习JS和React,我在这里真的卡住了!async在第一个dispatch之前。 - khateeb
SO说我可以在22小时后授予您赏金。 - khateeb
@khateeb,刚看到你的评论,我已经快更新完示例代码了,现在已经完成并发布在这里了,很高兴它能正常工作! - Brian Adams

-1
你可以像这样做:
fetch(url)
 .then((response) => response.json())
 .then(async (data) => {
    const categories = data.subCategories
             .map(category => new Category(category.id, category.name, category.type, category.url))

    for(category of categories) {
       let assets = await fetch(category.url).then(rsp => rsp.json())
       assets = assets.map(asset => Asset(assets.id, assets.title, assets.type, ass.thumbnail));
       category.assets = assets;
    }

    return categories
 }).then(categoriesWithAssets => {/*done*/})

我遇到了一个“SyntaxError: await is a reserved word”的错误。是否有使用“axios”库的方法来解决这个问题? - khateeb
如果你正在使用async/await,为什么要混合使用这么多的then调用呢? - Bergi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接