Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does `setTimeout` doesn't work when callback is passed via yield?

In the following code:

function so() {
  console.log('inside the timer')
}

function* sogen() {
  const callback = yield;

  setTimeout(callback, 2000);

  return 1;
}

function() {
  var gen = sogen();
  gen.next(so), gen.next(so);
}()

Why am I never reaching function so?

like image 276
AnArrayOfFunctions Avatar asked Oct 16 '18 22:10

AnArrayOfFunctions


People also ask

Does setTimeout use callback?

Introduction to JavaScript setTimeout()The setTimeout() sets a timer and executes a callback function after the timer expires. In this syntax: cb is a callback function to be executed after the timer expires. delay is the time in milliseconds that the timer should wait before executing the callback function.

What happens when if setTimeout () call with 0ms?

If you call setTimeout() with a time of 0 ms, the function you specify is not invoked right away. Instead, it is placed on a queue to be invoked “as soon as possible” after any currently pending event handlers finish running.

How do I pass context to setTimeout?

If you're using TypeScript, you can pass the function as a parameter, like this: setTimeout(this. tip. destroy, 1000);

What does setTimeout 0 Do JavaScript?

setTimeout(callback, 0) executes the callback with a delay of 0 milliseconds. Open the demo and check the console. You'll notice that 'Resolved!'


2 Answers

tl;dr you need to wrap your IIFE with parentheses or to not use an IIFE at all.

Your use of generators is fine, and once you add the parens, everything works as normal.

Note that you don't really need a IIFE to run your code, but my answer below explains why what you have is not working.

Function declarations vs Function expressions

The main problem you have is in this code:

function() {
  var gen = sogen();
  gen.next(so);
  gen.next(so);
}()

This will produce an error similar to:

Uncaught SyntaxError: Unexpected token (

The problem here is that you are trying to use a function declaration as a function expression.

From MDN (emphasis mine):

A function expression is very similar to and has almost the same syntax as a function statement (see function statement for details). The main difference between a function expression and a function statement is the function name, which can be omitted in function expressions to create anonymous functions. A function expression can be used as a IIFE (Immediately Invoked Function Expression) which runs as soon as it is defined. See also the chapter about functions for more information.

This means that in order to execute a function immediately, you need to use a function expression rather than a statement.

One common way of writing a function expression is to wrap the function in parentheses:

function a() { return 'a'; } // Function declaration
(function b() { return 'b'; }) // Function expression

To convert that into an IIFE, you can add the () invocation parens at the end:

(function c() { return 'c'; })() // IIFE

which calls the function immediately. Note that I prefer to put the invocation parentheses inside the wrapping parens but this is just a stylistic choice and works in the same way:

(function c() { return 'c'; }()) // IIFE

Here's the code from the answer, plus the parens wrapping the IIFE:

function so() {
  console.log('inside the timer');
}

function* sogen() {
  const callback = yield;

  setTimeout(callback, 2000);

  return 1;
}

(function() {
  const gen = sogen();
  gen.next(so);
  gen.next(so);
}())

Alternatively, simply remove your IIFE:

const gen = sogen();
gen.next(so);
gen.next(so);

or if you need a function declaration, call the function on the next line:

function run() {
  const gen = sogen();
  gen.next(so);
  gen.next(so);
}
run();
like image 159
lucascaro Avatar answered Sep 28 '22 08:09

lucascaro


The snipped you provided should now be working (except for the syntax error in the IIFE). I have rewritten it for clarity.

function so() {
  console.log('inside the timer')
}

function* sogen()
{
  const callback = yield; // line 1
  setTimeout(callback, 2000); // line 2
  return 1; // line 3
}

Now let's see how using the iterator returned from sogen we can call so.

var iter = sogen();

We have created an iterator. Calling the next method of the iterator we can advance the execution of the sogen generator.

iter.next();

After this call the state of the iterator is now frozen on line 1 of sogen. The yield was encountered and {value: undefined, done: false} was returned from the .next() call. At this point we are ready to pass our callback.

iter.next(so);

We have passed the callback into the next method and the execution resumes at line 1. The callback variable has now the value of so function. Continuing onto line 2 - setTimeout is called. In two seconds our so function will be called. But before it is the code continues to line 3. The .next(so) call returns {value: 1, done: true}. Now we wait.

After two seconds you should see that inside the timer has been logged to the console.

like image 38
Niebieski Avatar answered Sep 28 '22 09:09

Niebieski