Await nothing? Using an empty await to break up a large synchronous function

陌路散爱 提交于 2021-02-08 08:52:31

问题


I have a rather simple question that I can't find an answer to.

I have an async function called foo that never awaits, so it's run synchronously. In foo is a long-running loop and it pushes a lot of data into a intermediary buffer. There is an async operation using RxJS that will pull and process data from that buffer, but it won't execute until my long running loop yields somehow, or when the loop is completely done pushing millions of data points into the intermediary buffer (not good).

Can I await undefined or a dummy value, so that foo can yield to events that are waiting? Or will the promise return immediately because it's immediately resolved with a dummy value, and the function won't yield?


回答1:


If you have a section of long running synchronous code that you want to yield more to other things, there are a number of ways to go about that in node.js, each with their own advantages/disadvantages:

  1. Move the long running synchronous code to another node.js process and communicate completion back to the main process. If you truly want your long running synchronous code to not interfere at all with the rest of what your node.js code is doing, this is the clearest way to do that. If you run this code regularly or run it from multiple requests, you can even create a work queue and multiple other processes to run the tasks. People that do time consuming things like image recognition or other complicated data analysis, all want to get that time consuming code out of the main node.js event loop entirely.

  2. Use the new Workers in node.js to move this long running synchronous code out of the main loop. This will come with some limitations since workers have to pretty much stand on their own (kind of like webWorkers in a browser) and communicate with the main thread with messaging.

  3. Break your synchronous code into small units of work and execute each unit on a timer tick. That allows the event loop to run other tasks between your units of work. This is typically a significant amount of work rewriting the code because you essentially have to create some sort of state machine and you have to break your processing into small enough work units that you can execute a work unit, have the state fully saved into some type of object, exit out, let node.js do some other things, then on some sort of timer get called to execute another work unit. You can also use promises/await for this in some fashion, but promises have a somewhat higher priority so you may still be starving some other operations in your node.js code. A unit of work could also be executed on a call to a generator, where state could be saved in your generator function.

In most cases, there's the least amount of code rewriting if you pick option #1 or #2 though you don't really share much about what the actual code is doing for us to offer an opinion on that front ourselves. And, only #1 and #2 really get the long running CPU work out of the main node.js thread. Options #3, whether done with timers or promises, only allows for more interleaving with other tasks, but the CPU is still regularly bogged down with your synchronous work.

Can I await undefined or a dummy value, so that foo can yield to events that are waiting?

Sort of. Promise .then() handlers run at a bit higher priority than all events in the event loop so using an await to yield will allow some other things in the event loop to run (like other pending .then() handlers) but won't allow all other things to run (like pending timers). So, I wouldn't really recommend inserting an await on some constant as a solution for "fair" scheduling among all things in the event loop as it won't achieve that. It will allow some (but not all) other things to interleave with your work.

FYI, you can allow other timers to run by making your await, await a timer such as in this example, but you'd have to also test all the other types of things in the event loop (networking, disk I/O, etc...) to see if it allowed them to run:

    function delay(t) {
        return new Promise(resolve => {
            setTimeout(resolve, t);
        });
    }

    let start = Date.now();

    // long running function
    async function run() {
        let i = 0;
        let y;
        while(i++ < 1000000000) {
            for (let t = 0; t < 100; t++) {
                let x = 20;
                y = Math.pow(x,i);
            }
            await delay(1);
        }
        return i;
    }
    
    run();

    setInterval(() => {
        let delta = Date.now() - start;
        console.log((delta/1000).toFixed(3));
    }, 50);

My recommendation is still option #1 or #2 above to get another thread/process involved for your long running operation as that's always better for node.js.



来源:https://stackoverflow.com/questions/57300969/await-nothing-using-an-empty-await-to-break-up-a-large-synchronous-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!