Skip to content

Commit

Permalink
refactor(function): use thread instead of process
Browse files Browse the repository at this point in the history
closes #417
closes #416
  • Loading branch information
Kikobeats committed Oct 24, 2022
1 parent 1af45a1 commit 05f4bf4
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 29 deletions.
4 changes: 2 additions & 2 deletions packages/function/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
"@browserless/errors": "^9.6.6",
"debug-logfmt": "~1.0.4",
"deepmerge": "~4.2.2",
"execa": "~5.1.1",
"p-event": "~4.2.0",
"p-reflect": "~2.1.0",
"p-retry": "~4.6.1",
Expand All @@ -44,7 +43,8 @@
"devDependencies": {
"@browserless/test": "latest",
"ava": "3",
"browserless": "latest"
"browserless": "latest",
"lodash": "latest"
},
"engines": {
"node": ">= 12"
Expand Down
13 changes: 10 additions & 3 deletions packages/function/src/function.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const createVm = require('./vm')

const scriptPath = path.resolve(__dirname, 'function.js')

const { workerData, parentPort } = require('node:worker_threads')

const createFn = code => `
async ({ url, gotoOpts, browserWSEndpoint, ...opts }) => {
const { serializeError } = require('serialize-error')
Expand Down Expand Up @@ -42,9 +44,14 @@ async ({ url, gotoOpts, browserWSEndpoint, ...opts }) => {
}
}`

process.on('message', async ({ url, code, vmOpts, gotoOpts, browserWSEndpoint, ...opts }) => {
;(async () => {
const { url, code, vmOpts, gotoOpts, browserWSEndpoint, ...opts } = workerData

const vm = createVm(vmOpts)
const fn = createFn(code)
const run = vm(fn, scriptPath)
process.send(await run({ url, gotoOpts, browserWSEndpoint, ...opts }))
})

const result = await run({ url, gotoOpts, browserWSEndpoint, ...opts })

parentPort.postMessage(JSON.stringify(result))
})()
40 changes: 16 additions & 24 deletions packages/function/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
const { ensureError, browserTimeout } = require('@browserless/errors')
const debug = require('debug-logfmt')('browserless:function')
const requireOneOf = require('require-one-of')
const { driver } = require('browserless')
const { Worker } = require('worker_threads')
const pTimeout = require('p-timeout')
const pRetry = require('p-retry')
const pEvent = require('p-event')
const execa = require('execa')
const path = require('path')

const { AbortError } = pRetry
Expand All @@ -18,7 +17,7 @@ module.exports = (
fn,
{ getBrowserless = requireOneOf(['browserless']), retry = 2, timeout = 30000, ...opts } = {}
) => {
return async (url, fnOpts = {}) => {
return async (url, { workerOpts, ...fnOpts } = {}) => {
const browserlessPromise = getBrowserless()
let isRejected = false
let subprocess
Expand All @@ -28,30 +27,23 @@ module.exports = (
const browser = await browserless.browser()
const browserWSEndpoint = browser.wsEndpoint()

subprocess = execa.node(execPath, {
detached: process.platform !== 'win32'
const worker = new Worker(execPath, {
...workerOpts,
workerData: {
url,
code: fn
.toString()
.trim()
.replace(/;$/, ''),
browserWSEndpoint,
...opts,
...fnOpts
}
})

if (opts && opts.vmOpts && opts.vmOpts.console === 'inherit') {
subprocess.stderr.pipe(process.stderr)
subprocess.stdout.pipe(process.stdout)
}

debug('spawn', { pid: subprocess.pid })

subprocess.send({
url,
code: fn
.toString()
.trim()
.replace(/;$/, ''),
browserWSEndpoint,
...opts,
...fnOpts
})
debug('spawn', { pid: process.pid, thread: worker.threadId })
const { value, reason, isFulfilled } = JSON.parse(await pEvent(worker, 'message'))

const { value, reason, isFulfilled } = await pEvent(subprocess, 'message')
await driver.close(subprocess)
if (isFulfilled) return value
throw ensureError(reason)
}
Expand Down
16 changes: 16 additions & 0 deletions packages/function/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,19 @@ test('pass goto options', async t => {
t.false(isRejected)
t.is(value, '3.5.0')
})

test('interact with npm modules', async t => {
const code = async ({ page }) =>
require('lodash').toString(await page.evaluate('jQuery.fn.jquery'))

const fn = browserlessFunction(code, { vmOpts })
const { isFulfilled, isRejected, value } = await fn('https://example.com', {
gotoOpts: {
scripts: ['https://code.jquery.com/jquery-3.5.0.min.js']
}
})

t.true(isFulfilled)
t.false(isRejected)
t.is(value, '3.5.0')
})

0 comments on commit 05f4bf4

Please sign in to comment.