7

I have to work with a callback based API, but I would like to retain my async functions. That's why I'm trying to write a depromisify function:

const depromisify = fn => {
  if (!(fn[Symbol.toStringTag] === 'AsyncFunction')) {
    return fn;
  }

  // Can be `async` as the caller won't use assignment to get the result - it's all bound to the `cb`
  return async function () {
    const args = [...arguments];
    const cb = args.pop();

    try {
      return cb(null, await fn.apply(this, args));
    } catch (e) {
      return cb(e);
    }
  };
};

const normal = function(cb) {
  this.hello();
  cb(null, true);
};

const promised = async function() {
  this.hello();
  return true;
};

function Usual(fn) {
  this.random = 'ABC';
  fn.call(this, (err, result, info) => {
    console.log((err && err.message) || null, result);
  });
};

Usual.prototype.hello = () => {
  console.log('hello!');
};

new Usual(normal);
new Usual(depromisify(promised));

However it won't work when I try to depromisify an arrow function, as you can't bind anything to it:

new Usual(depromisify(async () => {
  this.hello();
  return false;
}));

Is there a solution for this?

Misiur
  • 4,355
  • 8
  • 32
  • 51
  • 1
    This has nothing to do with `async await` but that arrow functions are different to "regular" functions; at least in some regards. This code would not work like that, even if it was sync: `new Usual(() => { this.hello(); return false; });` – Thomas May 15 '18 at 19:29
  • Arrow functions can't be used as methods. There's no way out (apart from not using the `this` keyword, as in `var x = new Usual(() => x.hello())`, which of course won't work if `Usual` calls the function before it returns the instance). – Bergi May 15 '18 at 20:40
  • 1
    Btw, your `depromisify` is broken as it will run the callback twice when it throws a second time. Use [`.then(…, …)` with two callbacks instead](https://stackoverflow.com/a/44664037/1048572): `function depromisify(fn) { return function(...args) { var cb = args.pop(); fn.apply(this, args).then(v => cb(null, v), e => cb(e)).catch(e => void e); }; }` – Bergi May 15 '18 at 20:45
  • @Bergi Sorry about the duplicate, but what exactly do you have in mind with `throws a second time`? Reading the thread you linked, it does not seem like that is an equivalent situation, the `fn` can't throw twice, unless the wrapper itself throws an exception and there is no assignment to a value outside of `try...catch` – Misiur May 15 '18 at 21:07
  • Oops, messed up that sentence. I meant to say: it will run the callback a second time when it throws during the first call. Your `catch` should not handle exceptions from `cb`. – Bergi May 15 '18 at 21:11

1 Answers1

6

Nope. No solution. Arrow functions are a bit special in that regard.

Here is the quote from the docs:

Two factors influenced the introduction of arrow functions: shorter functions and non-binding of this.

RtmY
  • 7,115
  • 6
  • 51
  • 64
Randy Casburn
  • 11,404
  • 1
  • 12
  • 26