用Javascript伪同步编程,但是有更好的实现吗?

时间:2018-11-02 22:13:01

标签: javascript asynchronous synchronous

我有一个针对动漫(8到10岁)的JavaScript编码课程的原型。教学脚本允许其用户像

一样进行同步编程
draw(something);
wait(some time); 
draw(something_else);

同步编程看起来比常规的JavaScript技术友好得多,但是在后台是异步/等待的,并且不会使浏览器停滞不前。

您可以在http://codepegs.com/上尝试其工作原理,并在http://codepegs.com/cphelp.html上找到说明(滚动至定时功能)。

异步/等待非常有约束力。还有很多事情可以做,但是在重构系统之前,让我问一下,您是否知道可以使用JavaScript进行伪同步编程的现有解决方案?

我试图进行搜索,但答案似乎无济于事。这就是为什么我链接了一个示例。

PS:抱歉,我必须添加一个脚本。我要求提出一个更详细的实施方案。原则上,它是在一年前完成的。它很粗糙,但是有效。我不喜欢它有很多原因,但是我决定在开始新一轮编程之前先询问一下。

看,在脚本(或咒语)完成(异步)运行之后,它会留下按这种方式或另一种方式按时执行的功能序列。这样的列表可以完成很多事情。假设您想取消某些挂起的函数,或者想重复它们,也许更改某些东西,等等。这种伪同步模型并不是非常复杂,但并不是那么简单。想象一下,您想要一个无限的动画(已经实现,但是笨拙),等等。

3 个答案:

答案 0 :(得分:1)

没有可靠的方法来同步在前端JS中进行I / O。有同步的AJAX,但是WebWorkers,setTimeout,WebSockets等始终是异步的。

因此,您需要async/await才能获得同步样式的编码模式。

还有另一种方法,您基本上可以创建一个Promise队列:

enqueue(function foo(){}); // put on the queue
enqueue(function bar(){}); // put on the queue
enqueue(function baz(){}); // put on the queue

这意味着如果队列一次只处理一个函数调用,则foo,bar和baz将按顺序运行。但是,没有库就没有简单的方法可以将一个函数的结果传递给下一个函数。那就是承诺或异步库的用途。

要了解队列方法的工作原理,请查看以下库:

https://github.com/mapbox/node-sqlite3#usage

答案 1 :(得分:1)

按照Bergi的建议,以同步方式实际运行代码并将所有更改推送到队列中可能会更优雅,然后您可以缓慢地重放更改:

  const queue = [];

  function draw(text) { queue.push({command: "draw", value: text }); }
  function wait(value) { queue.push({ command: "delay", value }); }

  async function end() {
    for(const { command, value } of queue) {
       switch(command) {
          case "draw":
            console.log(value);
            break;
         case "delay":
            await new Promise(res => setTimeout(res, value));
            break;
      }
   }
}

// Your code:
draw("And you get...");
wait(1000);
draw("nothing :/");
end();

答案 2 :(得分:0)

万一其他失败,您仍然可以使用JavaScript本身编写自己的JavaScript转译器:

您可以使代码很有希望,然后只需将程序转换为使用async / await:

  function execute(context, code) {
    let result = "";
    for(const line of code.split("\n"))) {
      if(["if", "else", "var", "for", "while"].some(keyword => line.includes(keyword)) {
        result += line + "\n";
      } else {
        result += "await " + line + "\n";
      }
    }
    with(context) { 
       return eval(`(async function() { ${result} })()`);
     }
  }

然后将用户代码传递给执行:

  execute({
    async draw() { /*...*/ },
    async wait(ms) { await new Promise(res => setTimeout(res, ms)); },
  },
  `draw(something);
   wait(some time); 
   draw(something_else);`
 );

那只是为了给您一个基本的想法,但是it works quite well


或者另一种方法可能是建立一个迭代器,使您可以逐行浏览代码:

  let continuation = Promise.resolve();

  function wait(ms) { continuation = new Promise(resolve => setTimeout(resolve, ms)); }
  function draw() { /*...*/ }

   function execute( code) {
     const iterator = eval("(function* () {" + code.split("\n").join("\nyield;\n") + "})();");
    function next() {
      const { value, done } = iterator.next();
      if(done) return;
      continuation.then(next);
    }
 }

  execute(`
    console.log("wait a second....");
    wait(1000);
    console.log("done!");
 `);

works也是如此,我想它实际上是一个很好的工作方式,只有在代码包含多行字符串/函数的情况下,它才会失败。