带功能的同步代码

时间:2015-12-25 01:42:12

标签: javascript promise generator

以下函数允许使用sleep函数创建同步代码块,而不会阻塞。

function synchronous(generator)
{
   var _generator = generator();
   function done()
   {
      var result = _generator.next().value;
      if (result instanceof Promise)
         result.then(done);
   }
   done();
}

function sleep(ms)
{ 
   return new Promise(function(res, rej) { setTimeout(res, ms); });
}


// Runs without blocking, as you expect
synchronous(function* ()
{
   console.log('a');
   yield sleep(2000);
   console.log('b');
});

但是,以下代码会同时调用函数c()d(),而不是在调用c()之前等待d()完成。

synchronous(function* ()
{
   Log('a');
   yield sleep(2000);
   Log('b');
   yield sleep(2000);

   synchronous(c);
   synchronous(d);

   function* c()
   {
      Log('c');
      yield sleep(2000);
      Log('d');
      yield sleep(2000);
   }

   function* d()
   {
      Log('e');
      yield sleep(2000);
      Log('f');
      yield sleep(2000);
   }
});

Prints a b c e d f

我如何解决此问题并在d()完成后c()启动,而不设置更复杂的承诺系统?

2 个答案:

答案 0 :(得分:2)

synchronous将暂停其自己的执行,但其父堆栈帧的执行。这就是将生成器与广义协同程序分开的原因。如果要挂起外部函数mid-block的执行,它必须自己是一个生成器并屈服于它自己的父堆栈帧。没有逃生舱将允许您遵从事件循环并恢复相同的代码块。

您只能暂停yield的执行,并且您只能从生成器(yield / asyncawait执行,但总体处理相同。

您可以查看this article了解详情。

答案 1 :(得分:2)

快速解决方案

首先,您不能使异步函数同步。异步是有毒,这意味着所有调用异步代码并期望按顺序运行的代码本身必须是异步的。在您的情况下,外部函数已经是异步的,因此您可以使用yield*

使其工作
synchronous(function* ()
{
   Log('a');
   yield sleep(2000);
   Log('b');
   yield sleep(2000);

   yield* c();
   yield* d();

   function* c()
   {
      Log('c');
      yield sleep(2000);
      Log('d');
      yield sleep(2000);
   }

   function* d()
   {
      Log('e');
      yield sleep(2000);
      Log('f');
      yield sleep(2000);
   }
});

良好的解决方案

您的synchronous功能可以替换为babelHelpers.asyncToGenerator

function asyncToGenerator(fn) {
  return function () {
    var gen = fn.apply(this, arguments);
    return new Promise(function (resolve, reject) {
      function step(key, arg) {
        try {
          var info = gen[key](arg);
          var value = info.value;
        } catch (error) {
          reject(error);
          return;
        }
        if (info.done) {
          resolve(value);
        } else {
          Promise.resolve(value).then(function (value) {
            step("next", value);
          }, function (err) {
            step("throw", err);
          });
        }
      }
      step("next");
    });
  };
}

此函数用于在定义(而不是在调用)处包装函数,如下所示:

var a = asyncToGenerator(function*() {
    yield sleep(100);
});
var b = asyncToGenerator(function*() {
    var c = asyncToGenerator(function*() {
        yield sleep(300);
    });
    var d = asyncToGenerator(function*() {
        yield sleep(300);
    });

    yield a();
    yield sleep(200);
    yield c();
    yield d();
});

b(); // start the whole process with b