Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check if a variable is a generator function? (e.g function* yield) [duplicate]

Whats a reliable way to check if a function is a generator, e.g.:

let fn = function* () {
    yield 100;
}

if (fn instanceof ??) {
   for (let value in fn()) {
       ...
   }
}

The only way I can think of is fn.toString().startsWith('function*') but that's extremely hacky and unreliable

context: nodejs 4+

like image 952
Chris Barrett Avatar asked Dec 05 '15 08:12

Chris Barrett


1 Answers

Erik Arvidsson makes a good point in this answer to an earlier version of this question (it didn't occur to me that it's a dupe), that since any function can return an iterator, there's little point in checking whether a function is a generator or not. That is, there's not much you can do with the information in practical terms, since non-generators can return iterators.


I was wrong before, there is a better way than your toString check (if for some reason you have a valid need to do it at all):

  1. (Once) Get the value of the default constructor of a generator function, which is specified here. It doesn't have a global like Function and such do.

  2. (Whenever you need to check) Check to see if your target function is instanceof that generator function constructor.

E.g.:

// Once
var GeneratorFunction = (function*(){}).constructor;

// Whenever you need to check
if (fn instanceof GeneratorFunction) {
    // Yep, it's a generator function
}

Old things ruled out:

I don't see anything in the specification that lets us directly access the [[FunctionKind]] internal slot.

The spec does say:

Unlike function instances, the object that is the value of the a GeneratorFunction’s prototype property does not have a constructor property whose value is the GeneratorFunction instance.

So in theory:

if (!fn.prototype.hasOwnProperty("constructor")) {
    // It's a generator function
}

but, that would be incredibly unreliable, as people do things like this all the time (although hopefully less so as people start using class):

function Foo() {
}
Foo.prototype = {
    method: function() {}
};

While that Foo.prototype object has a constructor property, it's inherited, not "own". We could do an in check, or a .constructor == fn check, but the above would still mis-identify it. You just can't trust constructor in the wild, people mess it up too much.

like image 115
T.J. Crowder Avatar answered Nov 15 '22 14:11

T.J. Crowder