deasync

repository·master·Indexed 21 days ago

https://github.com/abbr/deasync

A JavaScript wrapper of the Node.js event loop that turns asynchronous functions into synchronous ones. It provides tools like deasync() for wrapping conventional callback APIs, loopWhile() for unconventional async APIs, and sleep() for non-busy-wait blockage. Primarily intended for maintaining synchronous APIs when underlying implementations change from sync to async.

Tokens
1.6K
Snippets
7
Records
9
Agent score
27%

What's inside deasync

  1. When to use deasync

    master

    DeAsync is intended for niche use cases where you must maintain a synchronous API for backward compatibility while switching an underlying data source from synchronous (e.g., fs.readFileSync) to asynchronous (e.g., a database driver).

    Warning: Unlike pure JavaScript libraries that only solve syntactic issues like 'callback hell', DeAsync changes the actual code execution sequence by blocking the JavaScript layer. If you are only looking to clean up syntax, it is recommended to use standard async/await or other pure JS patterns instead.

  2. Install deasync via npm

    master

    To install deasync, run the following command. Note that because the core is written in C++, you may need node-gyp and compatible compilers installed on your system to compile the source code.

    npm install deasync
  3. Wrap async functions with conventional callback APIs

    master

    You can use deasync as a generic wrapper for asynchronous functions that follow the conventional Node.js callback signature: function(p1, ...pn, function cb(error, result){}).

    When wrapped, the function will block and return the result directly, or throw an error as an exception if the callback receives a non-null error.

    var deasync = require('deasync');
    var cp = require('child_process');
    var exec = deasync(cp.exec);
    
    try {
        // This now returns the result synchronously
        console.log(exec('ls -la'));
    } catch(err) {
        console.log(err);
    }
  4. Use sleep to implement non-busy-wait blockage

    master

    The deasync.sleep(ms) method acts as a wrapper for setTimeout. It allows you to implement a blocking wait that yields to the Node.js event loop, preventing the CPU from pegging at 100% (unlike a standard busy-wait while loop). This is useful when you want to poll for a condition to be met.

    function SyncFunction() {
      var ret;
      setTimeout(function() {
          ret = "hello";
      }, 3000);
    
      while(ret === undefined) {
        require('deasync').sleep(100);
      }
    
      return ret;    
    }
  5. Use loopWhile for unconventional async APIs

    master

    If an asynchronous function uses an unconventional API (for example, a callback that only returns a result without an error parameter: function asyncFunction(p1, function cb(res){})), use deasync.loopWhile(predicateFunc).

    Pass a predicateFunc that returns a boolean representing the loop condition. The execution will block until the predicate returns false (e.g., when a flag is toggled by the async callback).

    var done = false;
    var data;
    
    asyncFunction(p1, function cb(res) {
        data = res;
        done = true;
    });
    
    require('deasync').loopWhile(function() {
        return !done;
    });
    
    // data is now populated
  6. Convert asynchronous functions to synchronous using deasync()

    master

    The deasync function wraps an asynchronous function (one that accepts a callback as its last argument) and returns a new function that behaves synchronously. When the returned function is called, it executes the original asynchronous function and enters a loop that allows the Node.js event loop to continue processing until the callback is invoked. Once the callback is called, the loop terminates and the result is returned synchronously.

    const deasync = require('deasync');
    
    // An async function that takes a callback as the last argument
    function asyncTask(arg, cb) {
      setTimeout(() => {
        cb(null, `Result for ${arg}`);
      }, 100);
    }
    
    // Wrap it to make it synchronous
    const syncTask = deasync(asyncTask);
    
    // This now blocks until the timeout completes
    const result = syncTask('my-input');
    console.log(result); // 'Result for my-input'
  7. Control the event loop with loopWhile()

    master

    The loopWhile(pred) method allows you to manually run the Node.js event loop while a specific condition is met. You pass a predicate function pred that returns a boolean. As long as pred() returns true, deasync will trigger the event loop (via process._tickCallback() and the internal binding) to allow pending asynchronous tasks to progress.

    const deasync = require('deasync');
    
    let done = false;
    
    // Some async operation
    setTimeout(() => {
      done = true;
    }, 1000);
    
    // Manually loop while 'done' is false
    deasync.loopWhile(() => !done);
    
    console.log('Async operation finished.');
  8. Run the event loop once with runLoopOnce()

    master
    The runLoopOnce() method triggers a single tick of the event loop and executes the underlying deasync binding. This is useful for advancing the state of pending asynchronous operations by a single step.
  9. Use deasync.sleep() to pause execution synchronously

    master

    The deasync.sleep(timeout) method provides a way to pause the current execution thread for a specified number of milliseconds synchronously. It uses the internal deasync mechanism to allow the event loop to run while waiting, preventing the entire process from freezing while still blocking the current execution flow.

    const deasync = require('deasync');
    
    console.log('Starting sleep...');
    deasync.sleep(2000); // Sleeps for 2 seconds synchronously
    console.log('Finished sleeping.');