quickjs-emscripten icon indicating copy to clipboard operation
quickjs-emscripten copied to clipboard

Issue with getting a result from an async function call

Open ppedziwiatr opened this issue 2 years ago • 1 comments

Hey,

first of all - thanks for this great library.

Now - I'm having slight issues with getting a result from an async function call.

Full code example below (I took https://github.com/justjake/quickjs-emscripten#promises as a reference example).

I tried to write proper comments in the code, but to sum-up

  1. I'm creating an instance of the QJS context
  2. I'm evaluating the script with the definition of the "handle" function - a function that is than supposed to be called many times with different params
  3. I'm calling the "handle" function
  4. the "handle" is being logged in the console
  5. The "Result1" is being logged in the console
  6. The "Result2" (i.e. the result from "handle" function call) is NEVER logged in the console..

The full result from the console is:

❯ node tools/quickjs.mjs
QuickJS: handle
Result1

It works fine when the handle is not defined as async.

Now - am I'm doing here sth wrong - or is such usecase simply not yet supported by the library?

import {getQuickJS} from 'quickjs-emscripten';

// the example smart contract code loaded from Arweave blockchain
const code = `
(() => {
    async function handle(state, action) {
      // this is being logged
      console.log('handle');
      return 1;
    }
    return handle;
})();
`.trim();

async function main() {

  // 1. creating the QJS context
  const QuickJS = await getQuickJS();
  const vm = QuickJS.newContext();

  // 2. registering "console.log" API
  const logHandle = vm.newFunction("log", (...args) => {
    const nativeArgs = args.map(vm.dump)
    console.log("QuickJS:", ...nativeArgs)
  });
  const consoleHandle = vm.newObject();
  vm.setProp(consoleHandle, "log", logHandle);
  vm.setProp(vm.global, "console", consoleHandle);
  consoleHandle.dispose();
  logHandle.dispose();

  // 3. registering the "handle" function in a global scope
  const handle = vm.evalCode(code);
  vm.setProp(vm.global, 'handle', handle.value);

  // 4. calling the "handle" function
  const result = vm.evalCode(`(async () => {
       return await handle();
     })()`);

  // execute pending jobs - is it necessary here?
  vm.runtime.executePendingJobs();

  if (result.error) {
    console.log('Execution failed:', vm.dump(result.error));
    result.error.dispose();
  } else {
    const promiseHandle = vm.unwrapResult(result)
    // the below is being logged
    console.log("Result1");
    const resolvedResult = await vm.resolvePromise(promiseHandle)
    promiseHandle.dispose();
    const resolvedHandle = vm.unwrapResult(resolvedResult);
    // the below is NEVER logged
    console.log("Result2:", vm.getNumber(resolvedHandle));
  }

  vm.dispose();
}

main().finally();

ppedziwiatr avatar Jun 27 '22 11:06 ppedziwiatr

Perhaps we need executePendingJobs after resolvePromise?

justjake avatar Jul 12 '22 15:07 justjake

Perhaps we need executePendingJobs after resolvePromise?

I've missed your answer. Unfortunatelly this didn't help.. :-(

I've also tried sth stupid like:

const code = `
    async function handle(state, action) {
      // this is being logged
      console.log('handle');
      return 1;
    }
`.trim();

const executePendingJobsHandle = vm.newFunction("epj", (...args) => {
    console.log("inside executePendingJobsHandle");
    vm.runtime.executePendingJobs();
  });


  const consoleHandle = vm.newObject();
  vm.setProp(consoleHandle, "log", logHandle);
  vm.setProp(consoleHandle, "epj", executePendingJobsHandle);
  vm.setProp(vm.global, "console", consoleHandle);
  consoleHandle.dispose();
  logHandle.dispose();
  executePendingJobsHandle.dispose();
  
    const result = vm.evalCode(`(async () => {
  ${code} 
  return handle().then(() => {
    console.log('inside then');
    console.epj();
  });
})()`);

 if (result.error) {
    console.log('Execution failed:', vm.dump(result.error));
    result.error.dispose();
  } else {
    const promiseHandle = vm.unwrapResult(result);
    // the below is being logged
    console.log("Result1");
    vm.runtime.executePendingJobs();
    const resolvedResult = await vm.resolvePromise(promiseHandle)

    promiseHandle.dispose();
    const resolvedHandle = vm.unwrapResult(resolvedResult);
    vm.runtime.executePendingJobs();
    // the below is NEVER logged
    console.log("Result2:", vm.getNumber(resolvedHandle));
  }

It is logging:

QuickJS: handle
Result1
QuickJS: inside then
inside executePendingJobsHandle

I suppose it only works with promises created via vm.newPromise()?

ppedziwiatr avatar Nov 30 '23 09:11 ppedziwiatr