Handy snippet for forcing an asynchronous promised based library/function to run synchronously.
This works by spawning a child_process
using spawnSync
.
The sync-rpc
module, will handle this for us in node.
Note: You shouldn't use this for production server side code. Forcing sync functions will block the event loop and can result in much slower programs. In some cases, like in a CLI or in a simple script you are writing, this doesn't matter much.
Sometimes, you just need things to work without an entire refactor of a script/library to get it working. This is a quick and easy way to force async to sync.
npm install sync-rpc
// async-thing.js file
function asyncFunction() {
return (paramOne, paramTwo) => {
// Your async code here
return delay(2000).then(() => {
return {
one: `${paramOne}-value`,
two: `${paramTwo}-value`,
}
})
}
}
// Simulate a async promise delay
function delay(t, v) {
return new Promise((resolve) => {
setTimeout(resolve.bind(null, v), t)
})
}
module.exports = asyncFunction
Import the ./async-thing
file and wrap with sync-rpc
module.
This will pause execution and make the async code block and run sync.
const forceSync = require('sync-rpc')
const syncFunction = forceSync(require.resolve('./async-thing'))
// inside your thing that needs to be sync (for whatever reason)
const paramOne = 'foo'
const paramTwo = 'bar'
console.log('start')
const syncReturn = syncFunction(paramOne, paramTwo)
console.log('syncReturn', syncReturn)
// result after 2 seconds
// { one: `foo-value`, two: `bar-value` }
// Do the rest of your stuff with `syncReturn` value
https://repl.it/@DavidWells1/SecondaryUnnaturalArguments
This is an interesting approach using worker threads described by https://samthor.au/2021/block-nodejs-main-thread
// main.js
import { Worker, MessageChannel, receiveMessageOnPort } from 'worker_threads'
const {
port1: localPort,
port2: workerPort
} = new MessageChannel()
const shared = new SharedArrayBuffer(4)
// send the shared buffer and port to the Worker
const workerData = {
shared,
port: workerPort
}
const w = new Worker('./task.js', {
workerData,
transferList: [workerPort]
})
const int32 = new Int32Array(shared)
Atomics.wait(int32, 0, 0) // blocks until notified at int32[0]
const message = receiveMessageOnPort(localPort)
console.warn('got fetch data', message)
Tasks.js
import { workerData } from 'worker_threads'
import fetch from 'node-fetch'
const {
shared,
port
} = workerData
console.warn('fetching')
const text = await fetch('https://google.com').then((r) => r.text())
console.info('fetched a blog')
port.postMessage(text)
const int32 = new Int32Array(shared)
Atomics.notify(int32, 0)