I was in a tech group talking about JavaScript generators a while back, and I think it’s a deep misconception that many people associate generator with async.

What exactly does generator have to do with asynchrony? A number of early frameworks, such as CO, used it to simulate async/await, but does this mean that generator is related to asynchrony? I think the answer is no. There are many language features used in CO, such as if, functions, variables… Generator is just one of them.

Generator is indeed a key language feature for emulating async/await, but the correct causal relationship is that generator and async/await share a basic JS infrastructure: functions suspend execution and retain the execution environment.

In my opinion, generator is much more useful than async/await. The generator represents an abstraction of “infinite sequences”, or perhaps long sequences, that can lead to breakthroughs in programming thinking.

On the front page of the official website of Haskell, a very progressive functional language, there is this code:

primes = filterPrime [2..]
  where filterPrime (p:xs) =
          p : filterPrime [x | x <- xs, x `mod` p /= 0]
Copy the code

This is a piece of code that Haskell programmers are proud of. Its purpose is to generate a sequence of prime numbers, a logical structure that is difficult to replicate in most languages. The most important of these is the application of the concepts of deferred computation and infinite lists.

We try to analyze the logic of this Haskell code, [2..] Represents a sequence of integers from 2 to infinity. FilterPrime is a function that filters the sequence of integers specified by where. So the key code that can turn a sequence of integers into a sequence of prime numbers is filterPrime. So what did it do?

This code is incredibly short. First, let’s look at the parameters part. P :xs is the parameter for deconstructing assignment, which means that the first element of the list in the input is assigned p and the rest of the list is assigned XS.

The first call to filterPrime argument is [2..] In this case, p is 2 and XS is endless list [3..] .

So how does filterPrime filter p and XS into a list of prime numbers? Let’s look at this code:

[x | x <- xs, x `mod\` p /= 0] `Copy the code

The general meaning of this paragraph can be explained by a section of pseudo-code suitable for JS programmers to understand:

xs.filter(x= >x % p ! = =0)
Copy the code

It’s just filtering multiples of P from the list XS. Of course, XS is not a JavaScript native array, so it doesn’t have a convenient filter method.

So, what’s interesting is that this array is passed to the filterPrime recursion. Now that the multiples of P have been filtered out of the XS, the first number left must be prime. We continue to filter the multiples of the first element with filterPrime recursion. You can go on and find the next prime number.

Finally, the code P: means to concatenate p to the first in the list.

Is it possible to replicate such programming ideas in JavaScript?

The answer, of course, is yes, and the key is the Generator.

The first problem we have to solve is [2..] This is an endless list. JavaScript does not support endless lists, but we can use generator for this:

function *integerRange(from, to){
    for(let i = from; i < to; i++){
        yieldi; }}Copy the code

Next, the array filter does not work well with endless lists, so we need a filter function for endless lists, which looks like this:


function *filter(iter, condition) {
    for(let v of iter) {
        if(condition(v)) {
            yieldv; }}}Copy the code

Finally, filterPrime, which is not too difficult once you understand Haskell, is implemented as follows:

function* filterPrime(iter) {
    let p = iter.next().value;
    let rest = iter;
    
    yield p;
    for(let v of filterPrime(filter(iter, x= >x % p ! =0)))
        yield v;
}
Copy the code

Now that the code is ready, we can use JavaScript’s unique asynchronous ability to print this prime sequence:

function sleep(d){
    return new Promise(resolve= > setTimeout(resolve, d));
}
void async function(){
    for(let v of filterPrime(integerRange(2.Infinity))) {await sleep(1000);
        console.log(v);
    }
}();
Copy the code

Okay, a little bit more syntactic noise, but at this point we’ve implemented the same prime-number sequence algorithm as Haskell.

In addition to endless lists, the Generator is also good for wrapping apis that express the concept of “indefinite lists.” For example, we can wrap the regular expression exec to make it a generator.

function* execRegExp(regexp, string) {
    let r = null;
    while(r = regexp.exec(string)) {
        yieldr; }}Copy the code

When we use for… we can use for… Of structure. The following code shows a simple lexical analysis.

let tokens = execRegExp(/let|var|\s+|[a-zA-Z$][0-9a-zA-Z$]*|[1-9][0-9]*|\+|-|\*|\/|; |=/g."let a = 1 + 2;")
for(let s of tokens){
    console.log(s);
}
Copy the code

Is this API design cleaner and more elegant than the original?

You see generator is a language feature with such potential that it opens the door to “endless” mathematical concepts for javascripters. So forget about emulating asynchrony, and hopefully this article will give you some inspiration to use Generator for open source projects or for API design in production. Thanks for watching.