通过嵌套的异步请求循环异步请求

时间:2019-02-27 11:48:19

标签: javascript node.js ecmascript-6 async-await

在一种情况下,我正在调用具有分页的API。 我想做的是以下,一次一页。

  1. 调用API页面1
  2. 对于响应中的每个项目,调用Promise以获取更多数据并存储在数组中
  3. 将数组发送到API
  4. 重复直到所有页面完成

我目前所拥有的是以下内容,但是我不确定使如何处理变得很复杂。

export const importData = async() {
    const pSize = 15;
    const response = await getItems(pSize, 1);
    const noPage = Math.ceil(response.totalMerchandiseCount/pSize);

    for (let i = 1; i < noPage; i++) {
        const items = [];
        const data = await getItems(pSize, i);

        await async.each(data.merchandiseList, async(i, cb) => {
            const imageURL = await getImageURL(i.id, i.type);
            items.push({
                id: i.id,
                imageURL: imageURL,
            });
            cb();
        }, async() => {
            return await api.mockable('sync', items);
        });
    }
}

export const getImageURL = async(id, type) => {
    let url = `https://example.com/${id}`;

    return axios.get(url)
        .then((response) => {
            const $ = cheerio.load(response.data);

            // do stuff to get imageUrl

            return image;
        })
        .catch((e) => {
            console.log(e);
            return null;
        })
};

我目前遇到的问题是,它似乎要等到所有页面完成后才能调用api.mockable。此时项目也为空。

有人可以提出一种使它更整洁并帮助我使其正常工作的方法吗?

1 个答案:

答案 0 :(得分:2)

如果这一切都是串行的,那么您可以使用for-of循环:

export const importData = async() {
    const pSize = 15;
    const response = await getItems(pSize, 1);
    const noPage = Math.ceil(response.totalMerchandiseCount/pSize);

    for (let i = 1; i < noPage; i++) { // Are you sure this shouldn't be <=?
        const items = [];
        const data = await getItems(pSize, i);

        for (const {id, type} of data.merchandiseList) {
            const imageURL = await getImageURL(id, type);
            items.push({id, imageURL});
        }
        await api.mockable('sync', items);
    }
}

我还在那里添加了一些破坏性和简写属性。 :-)

如果只是串行页面,但您可以并行获取项目,则可以在项目上将for-ofmap替换为Promise.all

export const importData = async() {
    const pSize = 15;
    const response = await getItems(pSize, 1);
    const noPage = Math.ceil(response.totalMerchandiseCount/pSize);

    for (let i = 1; i < noPage; i++) { // Are you sure this shouldn't be <=?
        const data = await getItems(pSize, i);
        const items = await Promise.all(data.merchandiseList.map(async ({id, type}) => {
            const imageURL = await getImageURL(id, type);
            return {id, imageURL};
        }));
        await api.mockable('sync', items);
    }
}

与非async函数相比,map函数对async的调用可以稍微更有效:

export const importData = async() {
    const pSize = 15;
    const response = await getItems(pSize, 1);
    const noPage = Math.ceil(response.totalMerchandiseCount/pSize);

    for (let i = 1; i < noPage; i++) {
        const data = await getItems(pSize, i);
        const items = await Promise.all(data.merchandiseList.map(({id, type}) =>
            getImageURL(id, type).then(imageURL => ({id, imageURL}))
        ));
        await api.mockable('sync', items);
    }
}