Check if the function is a generator

I played with generators in Nodejs v0.11.2, and I wonder how I can verify that the argument of my function is a generator function.

I found this method typeof f === 'function' && Object.getPrototypeOf(f) !== Object.getPrototypeOf(Function) , but I'm not sure if this is good (and works in the future).

What is your opinion on this issue?

+48
javascript generator yield ecmascript-6
May 25 '13 at 23:50
source share
11 answers

We talked about this at TC39 personal meetings, and we intend not to disclose a way to determine whether a function is a generator or not. The reason is that any function can return an iterable object, so it does not matter if it is a function or a generator function.

 var iterator = Symbol.iterator; function notAGenerator() { var count = 0; return { [iterator]: function() { return this; }, next: function() { return {value: count++, done: false}; } } } function* aGenerator() { var count = 0; while (true) { yield count++; } } 

These two behave the same (minus .throw (), but can be added too)

+36
Oct 29 '13 at 14:00
source share

In the latest version of nodejs (I checked with v0.11.12) you can check if the name of the GeneratorFunction constructor is equal. I don’t know which version it came out, but it works.

 function isGenerator(fn) { return fn.constructor.name === 'GeneratorFunction'; } 
+41
Mar 24 '14 at 13:11
source share

I use this:

 var sampleGenerator = function*() {}; function isGenerator(arg) { return arg.constructor === sampleGenerator.constructor; } exports.isGenerator = isGenerator; function isGeneratorIterator(arg) { return arg.constructor === sampleGenerator.prototype.constructor; } exports.isGeneratorIterator = isGeneratorIterator; 
+9
Mar 21 '14 at 8:44
source share

this works in node and in firefox:

 var GeneratorFunction = (function*(){yield undefined;}).constructor; function* test() { yield 1; yield 2; } console.log(test instanceof GeneratorFunction); // true 

jsfiddle

But this does not work if you link the generator, for example:

 foo = test.bind(bar); console.log(foo instanceof GeneratorFunction); // false 
+7
Jul 23 '14 at 3:32
source share

The TJ Holowaychuk co library has a better function for checking if something is a generator function. Here is the source code:

 function isGeneratorFunction(obj) { var constructor = obj.constructor; if (!constructor) return false; if ('GeneratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName) return true; return isGenerator(constructor.prototype); } 

Link: https://github.com/tj/co/blob/717b043371ba057cb7a4a2a4e47120d598116ed7/index.js#L221

+6
Jun 16 '16 at 16:55
source share

In node 7, you can instanceof from constructors to detect generator functions and asynchronous functions:

 const GeneratorFunction = function*(){}.constructor; const AsyncFunction = async function(){}.constructor; function norm(){} function*gen(){} async function as(){} norm instanceof Function; // true norm instanceof GeneratorFunction; // false norm instanceof AsyncFunction; // false gen instanceof Function; // true gen instanceof GeneratorFunction; // true gen instanceof AsyncFunction; // false as instanceof Function; // true as instanceof GeneratorFunction; // false as instanceof AsyncFunction; // true 

This works for all circumstances in my tests. The above comment says that it does not work for function expressions of the generated generators, but I cannot reproduce:

 const genExprName=function*name(){}; genExprName instanceof GeneratorFunction; // true (function*name2(){}) instanceof GeneratorFunction; // true 

The only problem is that the .constructor property of instances can be changed. If someone really decided to cause problems, they can break it:

 // Bad people doing bad things const genProto = function*(){}.constructor.prototype; Object.defineProperty(genProto,'constructor',{value:Boolean}); // .. sometime later, we have no access to GeneratorFunction const GeneratorFunction = function*(){}.constructor; GeneratorFunction; // [Function: Boolean] function*gen(){} gen instanceof GeneratorFunction; // false 
+4
Nov 20 '16 at 13:01
source share

Mozilla javascript documentation describes the Function.prototype.isGenerator MDN API method. Nodejs doesn't seem to implement it. However, if you want to limit your code to the definition of generators only with function* (without returning iterative objects), you can increase it by adding it directly with a compatibility check:

 if (typeof Function.prototype.isGenerator == 'undefined') { Function.prototype.isGenerator = function() { return /^function\s*\*/.test(this.toString()); } } 
+2
Jan 07 '14 at 16:41
source share

As @Erik Arvidsson said, there is no standard way to check if a function is a generator function. But you can, of course, just check the interface, the generator function does:

 function* fibonacci(prevPrev, prev) { while (true) { let next = prevPrev + prev; yield next; prevPrev = prev; prev = next; } } // fetch get an instance let fibonacciGenerator = fibonacci(2, 3) // check the interface if (typeof fibonacciGenerator[Symbol.iterator] == 'function' && typeof fibonacciGenerator['next'] == 'function' && typeof fibonacciGenerator['throw'] == 'function') { // it safe to assume the function is a generator function or a shim that behaves like a generator function let nextValue = fibonacciGenerator.next().value; // 5 } 

Here it is.

+2
Jun 07 '17 at 12:53 on
source share

I checked how koa does this and they use this library: https://github.com/ljharb/is-generator-function .

You can use it like this:

 const isGeneratorFunction = require('is-generator-function'); if(isGeneratorFunction(f)) { ... } 
+1
Jun 07 '17 at 11:33
source share

The difficulty, which is not mentioned here, is that if you use the bind method for the generator function, it changes the name of its prototype from "GeneratorFunction" to "Function".

There is no neutral Reflect.bind method, but you can get around this by dropping the prototype of the associated operation with the original operation.

For example:

 const boundOperation = operation.bind(someContext, ...args) console.log(boundOperation.constructor.name) // Function Reflect.setPrototypeOf(boundOperation, operation) console.log(boundOperation.constructor.name) // GeneratorFunction 
0
Apr 09 '17 at 16:51 on
source share

The old school Object.prototype.toString.call(val) seems to work as well. In Node version 11.12.0, it returns [object Generator] but the latest Chrome and Firefox return [object GeneratorFunction] .

It could be like this:

 function isGenerator(val) { return /\[object Generator|GeneratorFunction\]/.test(Object.prototype.toString.call(val)); } 
0
May 18 '19 at 10:53
source share



All Articles