javascriptnode.jstypescriptsettimeout

JavaScript setTimeout not reflecting updated variable value


I'm encountering an issue with setTimeout function where it doesn't seem to reflect the updated value of a variable.

Here's a simplified version of my code:

const handleProcessError = (processName, process) => {
    let responseTimeout;
    let responseAlreadySent = false;

    const sendResponse = (status, message, scan_id, scantype, error) => {
        if (!responseAlreadySent) {
            responseAlreadySent = true;
            clearTimeout(responseTimeout);
            if (status === 'Success') {
                console.log('Sending success response', responseAlreadySent);
                res.status(200).json({ status, message });
            } else {
                console.log('Sending error response', responseAlreadySent);
                res.status(500).json({ status, message });
                revertScanAndThrowError(error, processName);
            }
        } else {
            console.log('Response already sent, skipping');
        }
    };

    responseTimeout = setTimeout(() => {
        sendResponse('Success', 'The scan started', inc_val, scantype);
    }, 5000);
    
    let errorOutput = '';
          
    process.stderr.on('data', (data: Buffer) => {
        errorOutput += data.toString();
    });

    process.on('exit', (code: number, signal: NodeJS.Signals) => {
        if (code !== 0) {
            sendResponse('Error', 'Failed to start the scan', null, null, new Error(`Process exited with code ${code}. Error output: ${errorOutput}`));
        } else {
            console.log(`${processName} process exited with code 0`);
        }
    });

    // Other event listeners and handlers...
};

In this code, handleProcessError sets up a timeout function using setTimeout. When an error occurs, it calls sendResponse, which updates the responseAlreadySent flag to true. However, the timeout function still executes, even though the flag has been updated, leading to unexpected behavior.


Solution

  • This is a scope issue. Each time that sendResponse runs it sees its own version of responseAlreadySent. You can fix this by declaring the variable in a scope that is outside of handleProcessError. Here's a simplified version showing this

    var responseAlreadySent = false;
    const handleProcessError = (processName, process) => {
        let responseTimeout;
    
        const sendResponse = () => {
            console.log(responseAlreadySent);
            
            if (!responseAlreadySent) {
                responseAlreadySent = true;
                clearTimeout(responseTimeout);
    
                console.log('Sending response');
            } else {
                console.log('Response already sent, skipping');
            }
        };
    
        responseTimeout = setTimeout(() => {
            sendResponse();
        }, 5000);
        
        // Other event listeners and handlers...
    };