0

I previously relied on the following definition of how to detect a generator:

function isGenerator(func) {
    return func instanceof Function && func.constructor.name === 'GeneratorFunction';
}

Since then however I came across various other verifications, including checks for next and throw functions on the prototype.

Is there a way to implement isGenerator function to be considered comprehensive within the realm of NodeJS 0.10 - 5.x?

The application in which I intend to use is to pick up any generator (or even iterator), and wrap it up into a promise that would make use of functions next and throw.

For that I'm using a modified example from here:

/////////////////////////////////
// Generator-to-Promise adapter;
function asyncAdapter(generator) {
    return function () {
        var g = generator.apply(this, arguments);

        function handle(result) {
            if (result.done) {
                return Promise.resolve(result.value);
            }
            return Promise.resolve(result.value)
                .then(function (res) {
                    return handle(g.next(res));
                }, function (err) {
                    return handle(g.throw(err));
                });
        }

        return handle(g.next());
    }
}
vitaly-t
  • 24,279
  • 15
  • 116
  • 138
  • 0.10 doesn't have native generator functions (or does it?). Do you also want to detect transpiled functions (e.g. with generator)? – Felix Kling Dec 19 '15 at 02:24
  • I thought that since generators are built on just the two basic functions - `next` and `throw`, it was possible to have generators in the old Node JS also. But i'm just guessing here. – vitaly-t Dec 19 '15 at 02:25
  • A generator function won't have `next` or `throw` IIRC, its iterator will though. – MinusFour Dec 19 '15 at 02:28
  • 1
    Yeah, I think you are confusing generator functions with iterators. – Felix Kling Dec 19 '15 at 02:30
  • @MinusFour, that is fine. In my application I'm just wrapping up a generator into a promise, relying on just 2 functions - `next` and `throw`. If it still works in that context, that it's all good :) – vitaly-t Dec 19 '15 at 02:30
  • @FelixKling, you may be right. I will update my question to explain how I intend to use it. – vitaly-t Dec 19 '15 at 02:31

2 Answers2

1

I previously relied on the following definition of how to detect a generator […]

That doesn't detect generators, it detects generator functions. And you should not rely on such a thing, as every function can return a generator.

Is there a way to implement isGeneratorFunction to be considered comprehensive within the realm of NodeJS 0.10 - 5.x?

Yes, there are a few, next to the .constructor.name approach you've shown one can also use instanceof or .toString().

The application in which I intend to use is to pick up any generator (or even iterator)

Don't do this by magic. Just let the user decide whether they want to use generators, and let them call your function explicitly in that case.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • The link you provided is to the mess I referred to. So many different opinions are posted there. I guess I will just have to stick with my old implementation. – vitaly-t Dec 19 '15 at 17:11
  • @vitaly-t: The `instanceof GeneratorFunction` approach seems to be the best if you really need to distinguish them. But my point was that you should not need this at all. – Bergi Dec 19 '15 at 18:02
0

ES 6 says

9.2.6 GeneratorFunctionCreate (kind, ParameterList, Body, Scope, Strict)

The abstract operation GeneratorFunctionCreate requires the arguments: kind which is one of (Normal, Method), a parameter list production specified by ParameterList, a body production specified by Body, a Lexical Environment specified by Scope, and a Boolean flag Strict.

GeneratorFunctionCreate performs the following steps:

  1. Let functionPrototype be the intrinsic object %Generator%.
  2. Let F be FunctionAllocate(functionPrototype, Strict, "generator").
  3. Return FunctionInitialize(F, kind, ParameterList, Body, Scope).

so you could get close by checking that the prototype is that appropriate for a generator. Object.getPrototypeOf lets us test whether the value of functionPrototype is the builtin %Generator% though proxy objects can control the result of Object.getPrototypeOf. Treating proxies of generators as generators might be a good thing.

function isGenerator(functionThatMightBeGenerator) {
  function *nullGenerator() {}
  return (functionThatMightBeGenerator
          &&  Object.getPrototypeOf(functionThatMightBeGenerator)
          === Object.getPrototypeOf(nullGenerator));
}

If you're concerned about distinguishing between proxies of generators and generators, you might look at 14.5.14#6.g.i. That requires that class declaration forbids generators as super types so provides a direct check whether the FunctionKind internal slot is "generator".

This is probably overkill, but something like

function isGeneratorAndProbablyNotProxyThereof(functionThatMightBeGenerator) {
  function *nullGenerator() {}
  if (!functionThatMightBeGenerator
      || (Object.getPrototypeOf(functionThatMightBeGenerator)
          !== Object.getPrototypeOf(nullGenerator))) {
    return false;
  }
  function defineAndThrowawaySubClass(superType) {
    class C extends superType {}
  }
  let isGenerator = false;
  try {
    defineAndThrowawaySubClass(functionThatMightBeGenerator);
  } catch (e) {
    isGenerator = e instanceof TypeError;
  }
  return isGenerator;
}

should do it though a sufficiently motivated proxy writer could probably spoof this too.

Community
  • 1
  • 1
Mike Samuel
  • 118,113
  • 30
  • 216
  • 245