How can one access an unknown executer function from a Promise instance's construction time?

103 Views Asked by At

Is it possible to retrieve the implementation details of a Promise instance? Let's assume the following created promise ...

let myPromise = new Promise(function (success, error) {
  /* implementation */
}

Can one somehow access the anonymous function ...

function (success, error) {
  /* implementation */
}

... or get the location of the anonymous function in the code file?

2

There are 2 best solutions below

0
Bergi On BEST ANSWER

No. The promise object does not contain the code, neither internally nor accessible to JavaScript. A promise represents the result of a task, not the task itself, it is not runnable. The function is used only during construction.

0
Peter Seliger On

What the OP tries to achieve can be done by patching the global Proxy function before any other code starts using it.

The next provided example code, for demonstration purposes, stretches an overwriting and function-wrapping interception and introspection approach as far as one can go.

One has to do three things ...

  • reassign the Promise constructor function with a proxied version of itself, where the invocation of its internal [[Construct]] slot gets handled by custom code.

  • wrap intercepting custom code around the Promise constructor's executer function.

  • wrap intercepting custom code around the executer function's resolve function.

Having access to the Proxy constructor's executer function and logging it, as well as having access to the arguments passed to the resolve function might reveal some of the executer function's implementation details, thus being possibly able to provide some hints about where to drill down next.

// - The actual use case, an asynchronously implemented
//   `wait` functions which resolves after the provided
//   amount of milliseconds.
// - It uses the proxied version of the before created
//   and reassigned global `Proxy` constructor function.

async function waitForResolvedData(valueInMSec) {
  console.log('... executing ...\n\n');

  return new Promise(resolve =>
    setTimeout(resolve, valueInMSec, { foo: 'Foo', bar: 'Bar'})
  );
}

(async () => {
  console.log('waiting ...');

  const data = await waitForResolvedData(3000);

  console.log('\n... awaited data ...', data);
})();
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>

// - reassign the `Promise` constructor function
//   with a proxied version of itself, where the
//   invocation of its internal `[[Construct]]`
//   slot gets handled.

window.Promise = (Promise => {

  const handlers = {
    construct(target, [executer]) {

      console.log(
        `\`${ target.name }\` creation with following \`executer\` function ...\n`, executer
      );

      // - wrap intercepting custom code around the
      //   `Promise` constructor's `executer` function.
      executer = (proceed => {

        return ((/*resolve, reject*/...args) => {
          let [ resolve, reject ] = args;

          // - wrap intercepting custom code around the
          //   `executer` function's `resolve` function.
          resolve = (proceed => {
            return ((...args) => {

              console.log('`\nresolve` function intercepted with following args ...\n', args);
              console.log('original `resolve` function ...\n', proceed);

              proceed(...args);
            });
          })(resolve);

          console.log('`executer` intercepted with following args ...\n', args);
          console.log('original `executer` function ...\n', proceed);

          proceed(resolve, reject);
        });
      })(executer);

      return new target(executer);
    },
  };  
  return new Proxy(Promise, handlers);

})(window.Promise);

</script>