Njs: aggregated request with Promise.all() or async/await

Created on 31 Oct 2020  路  8Comments  路  Source: nginx/njs

I would like to make an aggregated request, in particular I would like to make an initial request and several subrequest depending on the response of the initial request. Then I would like to return the result of all subrequest.

I used an approach with Promise.all():

function get_orders_with_places(r) {
    r.subrequest('/orders')
    .then(response => get_places(JSON.parse(response.responseBody), r))
    .catch(error => r.return(error.stack));
}
function get_places(orders, r) {
    var promises = [];
    for (var i = 0; i < orders.length; i++) {
        orders[i].warehousePlaces = [];
        promises.push(r.subrequest('/location/' + orders[i].productName)
        .then(function(response) {
            var pallets = response.responseBody;
            for (pallet in pallets) {
                orders[i].warehousePlaces.push(pallet.storageLocation);
            }
        })
        );
    }
    Promise.all(promises).then(() => r.return(200, JSON.stringify(orders)));
}



md5-13e82dc227da5bd072bb53a9f293696c



async function get_places(orders, r) {
    for (var i = 0; i < orders.length; i++) {
        orders[i].warehousePlaces = [];
        var  pallets = await r.subrequest('/location/' + orders[i].productName);
        for (pallet in pallets) {
            orders[i].warehousePlaces.push(pallet.storageLocation);
        }
    }
    r.return(200, orders);
}

But I think async/await is not supported in njs.

I kind of ran out of ideas here. So any help would be much appreciated,

feature

Most helpful comment

Hi @Jurilz

I think that I can implement Promise.all().

All 8 comments

Hi @Jurilz

I think that I can implement Promise.all().

Hi @Jurilz

I think that I can implement Promise.all().

That would be really awesome and help me a lot.

Thank you.

@Jurilz

Here's the simplified Promise.all() polyfill:

function all(promises) {
    return new Promise((resolve, reject) => {
        var n = promises.length;
        var rs = Array(n);
        var done = () => {
            if (--n === 0) {
                resolve(rs);
            }
        };
        promises.forEach((p, i) => {
            p.then((x) => { rs[i] = x; }, reject).then(done);
        });
    });
}

all([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)])
.then((rs) => console.log(rs));
Promise { <pending> }
> [ 1, 2, 3 ]

@lexborisov

And Promise.race() pls.

Here's the simplified Promise.all() polyfill:

Thanks @drsm for your polyfill, I really think it should do the trick, but strangely enough I get a 500 Internal Server Error response from my subrequest-endpoint, although the /orders and the /location/ endpoint work fine.

I'm using it like this:

function get_orders_with_places(r) {
    r.subrequest('/orders')
    .then(response => get_places(JSON.parse(response.responseBody), r))
    .catch(error => r.return(error.stack));
}

function get_places(orders, r) {
    var promises = [];
    for (var i = 0; i < orders.length; i++) {
        orders[i].warehousePlaces = [];
        promises.push(r.subrequest('/location/' + orders[i].productName)
        .then(function(response) {
            var pallets = response.responseBody;
            for (pallet in pallets) {
                orders[i].warehousePlaces.push(pallet.storageLocation);
            }
        })
        );
    }
    resolveAll(promises)
    .then(() => r.return(200, JSON.stringify(orders)));
}

function resolveAll(promises) {
    return new Promise((resolve, reject) => {
        var n = promises.length;
        var rs = Array(n);
        var done = () => {
            if (--n === 0) {
                resolve(rs);
            }
        };
        promises.forEach((p, i) => {
            p.then((x) => { rs[i] = x; }, reject).then(done);
        });
    });
}

Unfortunately I can't narrow down the error, even if I write a catch after every .then() I don't get the error stack.

@Jurilz

-    resolveAll(promises)
+    return resolveAll(promises)

related to #346

And the reason:

$ node
Welcome to Node.js v12.19.0.
Type ".help" for more information.
> .editor
// Entering editor mode (^D to finish, ^C to cancel)
for (var i = 0; i < 10; ++i) {
Promise.resolve().then(() => console.log(`i = ${i}`));
}
Promise { <pending> }
> i = 10
i = 10
i = 10
i = 10
i = 10
i = 10
i = 10
i = 10
i = 10
i = 10

Related to #105

It's better to avoid loops when doing promises (without async/await):

function get_places(orders, r) {
    var on_location = (order, response) => {
        var pallets = JSON.parse(response.responseBody);
        order.warehousePlaces = pallets.map((x) => x.storageLocation);
    } 

    return resolveAll(
        orders.map((x) => {
            x.warehousePlaces = [];
            return r.subrequest('/location/' + x.productName).then(on_location.bind(null, x));
        })
    )
    .then(() => r.return(200, JSON.stringify(orders));
}

It's better to avoid loops when doing promises (without async/await):

You Sir, just made my day. Everything works as intended with your approach. And I additionally learned something about promises.

Thank you very much. 袘芯谢褜褕芯械 褋锌邪褋懈斜芯.

@lexborisov Hi! Thank you help for implementation Promise.all https://github.com/nginx/njs/issues/352#issuecomment-719942062
Excuse for troubling. Do you have any news about this issue?

Was this page helpful?
0 / 5 - 0 ratings