当通过yield传递回调时,为什么setTimeout不起作用?

时间:2018-10-16 22:10:41

标签: javascript typescript asynchronous yield

在以下代码中:

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);
}()

为什么我从不使用功能so

4 个答案:

答案 0 :(得分:4)

tl; dr;您需要在IIFE上加上括号,或者根本不使用IIFE。

您可以使用生成器,并且一旦添加了括号,一切都将正常运行。

请注意,您实际上并不需要IIFE来运行代码,但是下面的答案解释了为什么您的内容无法正常工作。

函数声明与函数表达式

您遇到的主要问题是这段代码:

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

这将产生类似于以下内容的错误:

  

未捕获到的SyntaxError:意外令牌(

这里的问题是您试图将function declaration用作function expression

来自MDN(重点是我):

  

函数表达式与函数语句非常相似,并且语法几乎相同(有关详细信息,请参见函数语句)。函数表达式和函数语句之间的主要区别是函数名称,可以在函数表达式中省略该名称以创建匿名函数。 函数表达式可以用作IIFE(立即调用函数表达式),该函数表达式在定义后立即运行。另请参阅关于功能的章节以获取更多信息。

这意味着要立即执行功能,您需要使用功能 expression 而不是 statement

编写函数表达式的一种常见方法是将函数包装在括号中

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

要将其转换为IIFE,可以在末尾添加()调用括号:

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

将立即调用该函数。 请注意,我更喜欢将调用括号放在包装括号内,但这只是一种样式选择,并且以相同的方式工作:

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

以下是答案中的代码,以及包裹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);
}())

或者,只需删除IIFE:

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

或者如果您需要函数声明,请在下一行调用函数:

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

答案 1 :(得分:3)

您提供的片段现在应该可以正常工作(IIFE中的语法错误除外)。为了清楚起见,我已对其进行了重写。

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

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

现在让我们看看如何使用从sogen返回的迭代器来调用so

var iter = sogen();

我们创建了一个iterator。调用迭代器的next方法,我们可以推进sogen生成器的执行。

iter.next();

此调用之后,迭代器的状态现在冻结在sogen的第1行上。遇到yield,并且从{value: undefined, done: false}调用返回了.next()。现在,我们可以传递回调了。

iter.next(so);

我们已将回调传递到next方法中,并且执行从第1行恢复。callback变量现在具有so函数的值。继续到第2行-setTimeout被调用。两秒钟后,我们的so函数将被调用。但是在此之前,代码继续到第3行。.next(so)调用返回{value: 1, done: true}。现在我们等待。

两秒钟后,您应该看到inside the timer已登录到控制台。

答案 2 :(得分:2)

在您的代码段中,sogen不是常规函数,而是生成器,如*所示。

您可以调用生成器以获取基本上是迭代器的迭代器,可以使用方法.next()进行控制,并且每次遇到yield关键字时都可以暂停其执行。

var it = sogen();
it.next();

您在那里(const callback = yield)的分配将通过随后的.next()调用来解决,例如:

it.next(function myCallback() { /* ... */ });

然后,生成器将继续运行,直到下一个yield或函数结束为止。

答案 3 :(得分:0)

这里您将生成器函数用作观察者。

在创建生成器对象时,实际上并没有调用它。

首次调用gen.next(so)时,它充当对生成器的调用,并且忽略传递给它的值。(第一次调用将执行提升到第一个yield。)

第二次调用gen.next(so)yield会收到“ function so(){}”函数,其余代码将被执行。

请查看此链接以更清楚: generator function as observer

您的working snippet