adska
adska

Reputation: 47

Node.js multiple requests at same time and one result returning

Multiple users can call requests at the same time, so I want to do if one user calls to request and calculation of results is being started then when another user calls the same request results calculation is not being started, but wait for results which were asked for the first user. In other words, the calculation of the result should be started only if it is not 'locked' by another user, if it is - then waits for the result.

Edited

Code: enter image description here

Results:

enter image description here

Upvotes: 1

Views: 2062

Answers (2)

eol
eol

Reputation: 24565

To guarantee that the result is not calculated multiple times for concurrent requests, you need to implement some kind of locking mechanism (as you expected).

Here's a very basic example of what your code could look like, that simply pushes requests to a queue if the mutex is currently locked. If not the result gets calculated and for all pending requests a response with the calculated value will be sent:

const express = require('express');
const app = express();

class Mutex {
    constructor() {
        this.queue = [];
        this.locked = false;
    }

    lock() {
        return new Promise((resolve, reject) => {
            if (this.locked) {
                this.queue.push([resolve, reject]);
            } else {
                this.locked = true;
                resolve();
            }
        });
    }

    release(value) {
        if (this.queue.length > 0) {
            const [resolve, reject] = this.queue.shift();
            resolve(value);
        } else {
            this.locked = false;
        }
    }
}

const getResultsMutex = new Mutex();

function getResults() {
    return new Promise((resolve => {
        setTimeout(() => resolve(Math.random()), 5000);
    }))
}

function sendResponse(result, req, res) {
    res.send("The result is " + result);
}

app.get('/getresults', async (req, res) => {
    let result = await getResultsMutex.lock();
    if (!result) {
        result = await getResults();
    }
    sendResponse(result, req, res);
    getResultsMutex.release(result);
});

app.listen(4000, function () {
    console.log("Server is running at port 4000");
});

Upvotes: 1

SoulKa
SoulKa

Reputation: 195

Like this?

/** @type {[express.Request, express.Response][]} */
let requestQueue = [];

/**
 * @param {express.Request} req 
 * @param {express.Response} res 
 */
async function processRequest( req, res ) {
    var result = await GetResults();
    res.end(result);
    if (requestQueue.length !== 0) processRequest.apply(requestQueue.shift());
}

app.get("/getresults", (req, res) => {
    if (requestQueue.length !== 0) return requestQueue.push([req, res]);
    processRequest(req, res);
});

EDIT: If they should all receive the same result, then this code:

/** @type {Promise} */
let requestPromise = null;

app.get("/getresults", (req, res) => {
    if (requestPromise === null) requestPromise = GetResults().finally(() => requestPromise = null); // returns a promise
    res.send(await requestPromise);
});

Upvotes: 0

Related Questions