Неизвестное количество обещаний зависит от результата предыдущего обещания? - PullRequest
0 голосов
/ 06 ноября 2018

Я пытаюсь написать функцию, которая извлекает все данные пользователя из API. К сожалению, API возвращает только 50 частей данных за запрос. Чтобы получить следующую «страницу» результатов, необходимо сделать еще один запрос GET с дополнительным путем, указывающим страницу результатов.

(В моем случае API - это Imgur, а данные - это альбомы пользователя.)

Я пытаюсь сделать это с Обещаниями. Функция populateAlbumList успешно возвращает только первую страницу результатов .

Я пытался изменить его, чтобы получить больше страниц результатов в функции populateAlbumList2, но она работает неправильно.

Как мне заставить работать эти условно-вложенные обещания? (Я бы предпочел не использовать библиотеку типа bluebird / q, потому что я хочу понять саму концепцию и шаблон).

/**
 * Performs an AJAX get request to the Imgur API, retrieving all the albums owned by the user. When the albums are
 * populated, they are logged to the extension settings page's console.
 * @returns {Promise<void>}
 */
async function populateAlbumList() {
    const username = await getItemFromStorage(STORAGE_USERNAME, ERROR_STORAGE_USERNAME_NOT_FOUND);
    const ALBUMS_URL = `https://api.imgur.com/3/account/${username}/albums`;

    // Fetch the albums for the currently logged in user
    return fetch(ALBUMS_URL, {
        method: "GET",
        headers: {
            "Authorization": "Bearer " + CLIENT_ID,
            "Content-type": "application/json; charset=UTF-8"
        }
    })
        .then(response => response.json())
        .then(json => json.data)
        .then(albums => albums.forEach(album => addAlbumToPage(album)));
}

/**
 * Performs an AJAX get request to the Imgur API, retrieving all the albums owned by the user. When the albums are
 * populated, they are logged to the extension settings page's console.
 * @returns {Promise<Array>}
 */
async function populateAlbumList2() {
    const username = await getItemFromStorage(STORAGE_USERNAME, ERROR_STORAGE_USERNAME_NOT_FOUND);
    let ALBUMS_URL = `https://api.imgur.com/3/account/${username}/albums`;
    const allAlbums = [];
    let page = 0;
    const promises = [];

    await getAlbumsFromImgur()
        .then(() => console.log(allAlbums));

    function getAlbumsFromImgur() {
        if (page > 0) {
            ALBUMS_URL = `https://api.imgur.com/3/account/${username}/albums` + page;
        }

        promises.push(
            fetch(ALBUMS_URL, {
            method: "GET",
            headers: {
                "Authorization": "Bearer " + CLIENT_ID,
                "Content-type": "application/json; charset=UTF-8"
            }
        })
            .then(response => response.json())
            .then(json => json.data)
            .then(albums => {
                allAlbums.push(albums);

                if (albums.length >= 50) {
                    page++;
                    promises.push(getAlbumsFromImgur());
                }
            })
        );
    }
}

1 Ответ

0 голосов
/ 06 ноября 2018

Поскольку вы используете функцию async, вам не нужно иметь дело с обещаниями напрямую, просто используйте await и напишите свой логический поток. Во-первых, давайте применим это только к получению первой страницы, чтобы мы могли увидеть, как это упрощает функцию; см. *** комментарии:

async function populateAlbumList() {
    const username = await getItemFromStorage(STORAGE_USERNAME, ERROR_STORAGE_USERNAME_NOT_FOUND);
    const ALBUMS_URL = `https://api.imgur.com/3/account/${username}/albums`;

    // Fetch the albums for the currently logged in user
    // *** Use await to consume the promise
    const response = await fetch(ALBUMS_URL, {
        method: "GET",
        headers: {
            "Authorization": "Bearer " + CLIENT_ID,
            "Content-type": "application/json; charset=UTF-8"
        }
    });
    // Note: You have to check for errors
    if (!response.ok) {
        throw new Error("HTTP error " + response.status);
    }
    // Read and parse the JSON, get the `data` property from it using destructuring
    // *** Use await to consume the promise
    let { data: albums } = await response.json();
    // Add the albums; no need for `forEach` when we have `for-of` available to us
    for (const album of albums) {
        addAlbumToPage(album);
    }
}

Теперь давайте расширим эту функцию, чтобы она продолжала выполнять запросы для последующих страниц, пока не получит менее 50 результатов:

async function populateAlbumList() {
    const username = await getItemFromStorage(STORAGE_USERNAME, ERROR_STORAGE_USERNAME_NOT_FOUND);
    const ALBUMS_URL = `https://api.imgur.com/3/account/${username}/albums`;

    // Start on page 0
    let page = 0;
    let albums; // We'll need this in our while condition below
    do {
        // Fetch the albums for the currently logged in user,
        // adding in the page if page > 0
        const response = await fetch(
            page === 0 ? ALBUMS_URL : ALBUMS_URL + page, // Seems odd there's no `/` or `?page=` or similar
            {
                method: "GET",
                headers: {
                    "Authorization": "Bearer " + CLIENT_ID,
                    "Content-type": "application/json; charset=UTF-8"
                }
            }
        );
        if (!response.ok) {
            throw new Error("HTTP error " + response.status);
        }
        // Read and parse the JSON, get the `data` from it
        albums = (await response.json()).data;
        // Add the albums
        for (const album of albums) {
            addAlbumToPage(album);
        }
        ++page;

        // Keep going until we get fewer than 50 back
    } while (albums.length >= 50);
}

Обратите внимание, что я также добавил проверку, чтобы проверить, сработал ли fetch, чего не было в вашем исходном коде. Это не только вы, большинство людей забывают включить эту проверку (настолько, что я написал ее в своем маленьком анемичном блоге).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...