aldn0
aldn0

Reputation: 21

JS Pagination Using Promises

I'm attempting to make an API call using promises. The API is paginated and as such, depending on the headers in that first API call make more to get the rest of the results if need be.

Here's what I have so far:

const get = (url, pageNo) => {
    var options = {
        url: url,
        headers: {
            'Authorization': `Token token=${apiToken}`
        },
        json: true,
        page: pageNo
    };

    return new Promise((resolve, reject) => {
        request.get(options, (err, resp) => {
            err ? reject(err) : resolve(resp);
        })
    });
};

Using get() to loop and get all responses:

const getAll = (plannerId, timestamp, range) => {
    const plannerBookingsUrl = new URL(
        `/api/planners/${plannerId}/bookings?since=${timestamp}&range=${range}`,
        baseUrl
    );

    let response = get(plannerBookingsUrl, 1);
    let bookings = [];
    bookings.push(response);

    response.then(results => {
        let moreRequests = true;
        let currentPage = 1;
        const totalPages = parseInt(results.headers['x-total-pages']);

        while (moreRequests) {
            if (currentPage < totalPages) {
                nextBatch = get(plannerBookingsUrl, currentPage + 1);
                bookings.push(nextBatch);
                currentPage++;
            } else {
                moreRequests = false;
            }
        }
    });

    return Promise.all(bookings);
};

Main() where I'm using getAll(...):

const main = () => {
    const response = getAll(
        '11716',
        '2020-02-27',
        '7'
    );

    response.then(results => {
        console.log(results);
    .catch(error => console.log(error))
};

main();

This returns the initial promise but not the remaining promises.

What I'm really have a problem with is reading the first API, making the remainder and returning them all together to be using in my main function.

Any help would be much appreciated!

Thanks.

Upvotes: 2

Views: 2585

Answers (2)

Emiel Zuurbier
Emiel Zuurbier

Reputation: 20944

You could put all your fetching logic inside the while loop. The way you get your bookings is the same, except for the first time where you need to get a little more information on the amount of pages.

Accomplish this by making your function async and check the first time of the loop if the totalPages value is already known. If it's not, await the response and get the info from the headers, and otherwise just push the response to the bookings array.

const getAll = async (plannerId, timestamp, range) => {

    const plannerBookingsUrl = new URL(
        `/api/planners/${plannerId}/bookings?since=${timestamp}&range=${range}`,
        baseUrl
    );

    let bookings = [];
    let currentPage = 1;
    let totalPages = null;

    while (totalPages === null || currentPage < totalPages) {
        let response = get(plannerBookingsUrl, currentPage);
        if (totalPages === null) {
            let results = await response;
            totalPages = parseInt(results.headers['x-total-pages']);
        }
        bookings.push(response);
        currentPage++;
    }

    return Promise.all(bookings);

};

Upvotes: 1

zken
zken

Reputation: 1036

The problem is that you are returning Promise.all(bookings) outside response.then callback, so at this point bookings contains only the first call get(plannerBookingsUrl, 1).

Here is a possible solution using async:

const getAll = async (plannerId, timestamp, range) => {
    const plannerBookingsUrl = new URL(
        `/api/planners/${plannerId}/bookings?since=${timestamp}&range=${range}`,
        baseUrl
    );

    let response = get(plannerBookingsUrl, 1);
    let bookings = [];
    bookings.push(response);

    const results = await response; // wait for results here

    let moreRequests = true;
    let currentPage = 1;
    const totalPages = parseInt(results.headers['x-total-pages']);

    while (moreRequests) {
        if (currentPage < totalPages) {
            nextBatch = get(plannerBookingsUrl, currentPage + 1);
            bookings.push(nextBatch);
            currentPage++;
        } else {
            moreRequests = false;
        }
    }

    return Promise.all(bookings); // bookings now contains every next batch
};

adapt on main() function:

const main = async () => {
    const results = await getAll(
        '11716',
        '2020-02-27',
        '7'
    );

    ...
};

main();

Upvotes: 0

Related Questions