就JavaScript promise和C#asyc-await而言,什么是状态机?

Zac*_*ith 5 javascript c# promise async-await

我目前正在async-await使用C#进行浏览,并且注意到与JavaScript Promise的相似之处。对此进行研究,我发现JavaScript也将支持async-await语句,并且此语句和promise之间有相似之处(例如,请参见此博客文章)。

一时兴起,我想知道JavaScript的实现async-await是什么,并发现了这个问题(Java等效于C#async / await?)。

公认的答案表明async-await(并且扩展,我想是,诺言)是“状态机”的实现。

问题:在承诺方面,“状态机”是什么意思,JavaScript承诺是否可以与C#媲美async-await

Ben*_*aum 5

JavaScript 承诺类似于 C#Task对象,后者具有ContinueWith类似于.thenJavaScript的功能。

“状态机”是指它们通常由状态和 switch 语句实现。状态是函数在同步运行时可以处于的位置。我认为最好看看这种转换在实践中是如何运作的。例如,假设您的运行时仅理解常规函数。异步函数看起来像:

async function foo(x) {
   let y = x + 5;
   let a = await somethingAsync(y);
   let b = await somethingAsync2(a);
   return b;
}
Run Code Online (Sandbox Code Playgroud)

现在,让我们看看函数在同步执行步骤时可以出现的所有位置:

async function foo(x) {
   // 1. first stage, initial
   let y = x + 5;
   let a = await somethingAsync(y);
   // 2. after first await
   let b = await somethingAsync2(a);
   // 3. after second await
   return b;
   // 4. done, with result `c`.
}
Run Code Online (Sandbox Code Playgroud)

现在,由于我们的运行时只理解同步函数 - 我们的编译器需要做一些事情来使代码成为同步函数。我们可以将其设为常规函数并保持状态吗?

let state = 1;
let waitedFor = null; // nothing waited for
let waitedForValue = null; // nothing to get from await yet.
function foo(x) {
   switch(state) {
      case 1: { 
        var y = x + 5;
        var a;
        waitedFor = somethingAsync(y); // set what we're waiting for
        return;
      }
      case 2: {
         var a = waitedForValue; 
         var b;
         waitedFor = somethingAsync(a);
         return;
      }
      case 3: {
        b = waitedFor;
        returnValue = b; // where do we put this?
        return;
      }
      default: throw new Error("Shouldn't get here");
   }
}
Run Code Online (Sandbox Code Playgroud)

现在,它有点有用,但没有做任何太有趣的事情——我们需要将它作为一个函数来实际运行。让我们将状态放在一个包装器中,并在它们解决时自动运行承诺:

function foo(x) { // note, not async
  // we keep our state
  let state = 1, numStates = 3;
  let waitedFor = null; // nothing waited for
  let waitedForValue = null, returnValue = null; // nothing to get from await yet.
  // and our modified function
  function stateMachine() {
    switch(state) {
      case 1: { 
        var y = x + 5;
        var a;
        waitedFor = somethingAsync(y); // set what we're waiting for
        return;
      }
      case 2: {
         var a = waitedForValue; 
         var b;
         waitedFor = somethingAsync(a);
         return;
      }
      case 3: { 
        b = waitedFor;
        returnValue = b; // where do we put this?
        return;
      }
      default: throw new Error("Shouldn't get here");
   }
   // let's keep a promise for the return value;
   let resolve, p = new Promise(r => resolve = r); // keep a reference to the resolve
    // now let's kickStart it
   Promise.resolve().then(function pump(value) {
      stateMachine();
      state++; // the next state has progressed
      if(state === numStates) resolve(returnValue); // return the value
      return Promise.resolve(waitedFor).then(pump);
   }); 
   return p; // return the promise
}
Run Code Online (Sandbox Code Playgroud)

实际上,该Promise.resolve().then(...部分调用 stateMachine 并等待每次等待的值,直到它处于最终状态,此时它解决(预先返回的)承诺。

这实际上也是 BabelTypeScript对您的代码所做的。C# 编译器所做的非常接近——最大的不同在于它被放在一个类中。

请注意,我们在这里忽略了条件、异常和循环——这会使事情变得更复杂,但不会更难(您只需要分别处理每种情况)。