<p>I have recently been making my own programming language and I am finding most concepts easy to understand, however I'm not sure how to implement async/await in my language.</p>
<p>ATM, I'm using C# to create this language and I have no idea how to go about implementing such features.</p>
<p>The language is an interpreted one.</p>
<p>I'm able to implement a JS-like Promise system, but this doesn't help with running asynchronous C# functions, for example sending a HTTP request.</p>
<p>What I have working with such &quot;promise-like&quot; system is this:</p>
<pre><code>var any f = Future.create(|resolve, reject| =&gt; resolve(2));
f.then(val =&gt; Console.writeLine(val));
</code></pre>
<p>It works perfectly within the language, however trying to run async C# functions whilst the entire interpreter does not use async at all I have no idea how to do.</p>
<p>The only options I can think of is</p>
<ol>
<li>Make every C# function async (like the parser, interpreter, etc.) by default, but I'm not sure if this would help</li>
<li>Use the <code>.GetAwaitor().GetResult()</code> but apparently this shouldn't really be used</li>
</ol>
<p>I've tried searching and looking everywhere on Google for ways to implement this but I could never find any, so any resources for this topic would be appreciated.</p>

<language-design><implementation><async><c#>
Async Implementation
CC BY-SA 4.0
2023-12-21T22:49:07.090Z
I take it you're writing an interpreter? It would help to make this explicit. By the way, welcome to the site!
kaya3
CC BY-SA 4.0
2023-12-21T22:51:02.423Z
@kaya3 thanks, I've edited my question
InvaderIzzy
<blockquote>
<p>I'm able to implement a JS-like Promise system, but this doesn't help with running asynchronous C# functions, for example sending a HTTP request.</p>
</blockquote>
<p>Async/await is syntax sugar for promises.</p>
<p>You'll need to familiarize yourself with <a href="https://matt.might.net/articles/by-example-continuation-passing-style/" rel="noreferrer"><strong>Continuation Passing Style (CPS)</strong></a>. Basically, CPS is a way to write regular, sequential (&quot;procedural&quot;) code as a chain of callbacks. Ex:</p>
<pre class="lang-js prettyprint-override"><code>function() {
    let x = foo(&quot;a&quot;, &quot;b&quot;)
    let y = bar(&quot;c&quot;, &quot;d&quot;)
    let z = baz(1, 2, x)
    return y + z
}
</code></pre>
<p>converted into CPS is</p>
<pre class="lang-js prettyprint-override"><code>function(k) {
    foo(&quot;a&quot;, &quot;b&quot;, function(x) {
        bar(&quot;c&quot;, &quot;d&quot;, function(y) {
           baz(1, 2, x, function(z) k(y + z))
        })
    })
}
</code></pre>
<p>This is roughly what your interpreter needs to do with async functions before you interpret them. Convert:</p>
<pre class="lang-js prettyprint-override"><code>async function() {
    let x = await foo(&quot;a&quot;, &quot;b&quot;);
    let y = await bar(&quot;c&quot;, &quot;d&quot;);
    let z = await baz(1, 2, x);
    return y + z;
}
</code></pre>
<p>into</p>
<pre class="lang-js prettyprint-override"><code>function() { return new Promise(resolve =&gt; 
    foo(&quot;a&quot;, &quot;b&quot;).then(function(x) {
        bar(&quot;c&quot;, &quot;d&quot;).then(function(y) {
           baz(1, 2, x).then(function(z) resolve(y + z))
        })
    })
}
</code></pre>
<p>Specifically:</p>
<ul>
<li>After parsing an <code>async</code> function or other kind of <code>aysnc</code> block, desugar it into a regular function which returns a promise, where all the <code>await</code>s are replaced with <code>.then</code> and <code>.catch</code> callbacks.</li>
<li>If your language supports top-level async, you can parse and desugar the entire source to get one big promise which you then execute. Alternatively, to avoid parsing the entire file at once, you can parse and desugar each <em>individual statement</em> into a promise, <em>waiting to parse the next statement until the promise resolves</em>. Basically, write an async function <code>evalRemaining(int pos)</code>, which lexes/parses and desugars the first statement into a promise, then adds a <code>.then</code> callback to that promise which recursively calls <code>evalRemaining</code> with the position right after that statement's end.</li>
<li>If your language supports top-level async in a REPL you handle it similarly: read/lex/parse/desugar the current line into a promise, then wait until that promise resolves before asking the user for the next line, by putting the code to do so in the promise callback.</li>
</ul>
<p>Now, your goal is to desugar a statement or expression with <code>await</code>s, into a promise expression where the <code>await</code>s are replaced with <code>.then</code> and <code>.catch</code> callbacks, and the promise resolves into the expression result. This is essentially the same as converting a block of procedural code into a function written in CPS.</p>
<hr />
<p>There's a lot of existing literature on continuation passing style. You mentioned having trouble finding resources to implement async/await, but if you can rephrase your queries about implementing async/await into implementing CPS conversion, I guarantee you'll find online guides and other resources.</p>
<h1>Rule-based async/await (CPS-style) conversion via <a href="https://en.wikipedia.org/wiki/Tree_traversal#Pre-order,_NLR" rel="noreferrer">preorder traversal</a></h1>
<p>Like type-checking, computing the derivative in calculus, and other algorithms, one way to do async/await conversion is to create a fixed set of rules which handle general cases and compose into whatever code you may encounter. Specifically, create a rule for every kind of statement which desugars in a special way, and then to desugar an arbitrary statement, run a preorder traversal, processing each sub-statement or expression with the applicable rule.</p>
<p>Here are some of these rules. If your language has unique semantics, you may need to come up with other rules or modify these rules on your own. There are also be optimized rules for more specific cases which will generate faster / smaller code. Lastly, I didn't check these rules, so there may be bugs:</p>
<h2>Async function/block</h2>
<pre class="lang-js prettyprint-override"><code>async function f(...) -&gt; T {
    ...
    &lt;... throw x; ...&gt;
    &lt;... return y; ...&gt;
}
</code></pre>
<p>converts into</p>
<pre class="lang-js prettyprint-override"><code>function f(...) -&gt; Promise&lt;T&gt; {
    return new Promise((resolve, reject) =&gt; {
        ...
        &lt;... resolve(x); ...&gt;
        &lt;... reject(y); ...&gt;
        // In case the function doesn't return anything,
        // add this to the end of the Promise or it'll never resolve
        resolve()
    });
}
</code></pre>
<p>(e.g. <code>return foo</code> gets converted into <code>resolve(foo)</code>, <code>throw bar</code> gets converted into <code>reject(bar)</code>, otherwise code is desugared with the following rules and placed into the promise closure).</p>
<p>Async blocks, if your language supports them (like Rust), are similar:</p>
<pre class="lang-rust prettyprint-override"><code>async {
    ...
    result
}
</code></pre>
<p>converts into</p>
<pre class="lang-rust prettyprint-override"><code>Promise::new(|resolve| {
    ...
    resolve(result)
})
</code></pre>
<h3>Desugar from inside out (<a href="https://en.wikipedia.org/wiki/Tree_traversal#Pre-order,_NLR" rel="noreferrer">preorder traversal</a>)</h3>
<p>If a statement contains a nested <code>await</code> but it's in an <code>async</code> function or block, desugar the inner <code>async</code> function/block before desugaring the outer one. This also applies to <code>async</code> expressions and other rules.</p>
<h2>Statements or expressions without <code>await</code></h2>
<p>A sequence of statements without an await is unchanged.</p>
<pre class="lang-js prettyprint-override"><code>async function() {
    a;
    while (b) { c; d; }
    try { e; } catch { f; } finally { g; }
}
</code></pre>
<p>converts into</p>
<pre class="lang-js prettyprint-override"><code>function() -&gt; Promise&lt;()&gt; {
    return new Promise(resolve =&gt; {
        a;
        while (b) { c; d; }
        try { e; } catch { f; } finally { g; }
        resolve();
    });
}
</code></pre>
<h2>Expression or expression statement</h2>
<pre class="lang-js prettyprint-override"><code>before;
context &lt;... await expr ...&gt; context;
after;
</code></pre>
<p>becomes</p>
<pre class="lang-js prettyprint-override"><code>before;
expr.then(e =&gt; {
    context &lt;... e ...&gt; context;
    after
});
</code></pre>
<p>e.g.</p>
<pre class="lang-js prettyprint-override"><code>let a = foo();
let b = bar(await baz()) * 2;
let c = a + b;
</code></pre>
<p>becomes</p>
<pre class="lang-js prettyprint-override"><code>let a = foo();
baz().then(e =&gt; {
   let b = e * 2;
   let c = a + b;
});
</code></pre>
<h2>If statement</h2>
<pre class="lang-js prettyprint-override"><code>if (cond) {
    then;
} else {
    else;
}
rest;
</code></pre>
<p>If either <code>then</code> or <code>else</code> contain an <code>await</code>, the entire <code>if</code> must be converted into a new promise</p>
<pre class="lang-js prettyprint-override"><code>new Promise(resolveTheIf =&gt; {
    if (cond) {
        then.then(resolveTheIf);
    } else {
        else.then(resolveTheIf);
    }
}).then(() =&gt; {
    rest;
})
</code></pre>
<p>(of course, make sure <code>resolveTheIf</code> doesn't shadow the outer <code>resolve</code> in case <code>then</code> or <code>else</code> contains a <code>return</code> statement).</p>
<p>If <code>cond</code> contains an <code>await</code>, you can convert into the equivalent:</p>
<pre class="lang-js prettyprint-override"><code>let c = cond;
if (c) {
    then...
} else {
    else...
}
rest...
</code></pre>
<p>and then convert from there.</p>
<p>If either <code>then</code> or <code>else</code> does not contain an <code>await</code>, replace <code>then.then(resolveTheIf)</code> or <code>else.then(resolveTheIf)</code> with <code>then; resolveTheIf()</code> or <code>else; resolveTheIf()</code> (respectively).</p>
<p>If your <code>if</code>/<code>else</code> returns an expression like Rust, convert the zero argument call/closure into a one-argument one. Ex:</p>
<pre class="lang-rust prettyprint-override"><code>let foo = if (cond) {
    await then()
} else {
    else()
}
...
</code></pre>
<p>converts into</p>
<pre class="lang-rust prettyprint-override"><code>Promise::new(|resolveTheIf| {
    if (cond) {
        then.then(resolveTheIf);
    } else {
        resolveTheIf(else)
    }
}).then(|foo| {
    ...
})
</code></pre>
<h3>While statement</h3>
<p>Similar to the above</p>
<pre class="lang-js prettyprint-override"><code>while (cond) {
    body;
}
rest;
</code></pre>
<p>if <code>body</code> contains an `await, this converts into</p>
<pre class="lang-js prettyprint-override"><code>const recurse = resolveTheWhile =&gt; {
    if (cond) {
        body.then(() =&gt; recurse(resolveTheWhile));
    } else {
        resolveTheWhile();
    }
};
new Promise(recurse)
</code></pre>
<p>If <code>cond</code> contains an <code>await</code>, you can resolve it before the <code>while</code> block.</p>
<p>Like <code>return</code> and <code>throw</code> when desugaring an async function, <code>break</code> desugars into <code>resolveTheWhile()</code>. If you have nested <code>while</code>s and allow labeling them, so that users can call <code>break outer</code> from within the inner loop to break the outer one, you have to call <code>resolveTheWhile</code> of <code>outer</code> to break the outer loop.</p>
<h3>Switch and for statements</h3>
<p>These can be deduced from the <code>if</code> and <code>while</code> statements. I'll show an example of a complicated <code>for</code> statement.</p>
<pre class="lang-js prettyprint-override"><code>for (let i = await init(); await cond(i); await update(i)) {
    await body;
}
rest;
</code></pre>
<p>converts into</p>
<pre class="lang-js prettyprint-override"><code>init().then(i =&gt; {
    const recurse = resolveTheFor =&gt; {
        cond(i).then(c =&gt; {
            if (c) {
                body.then(() =&gt; update(i).then(() =&gt; recurse(resolveTheFor)));
            } else {
                resolveTheFor();
            }
        })
    };
}
</code></pre>
<h3>Try/catch/finally statement</h3>
<p>This is similar to the <code>if</code> statement, except that the <code>catch</code> block corresponds to a <code>.catch</code> promise callback.</p>
<pre class="lang-js prettyprint-override"><code>try {
    tryCode;
} catch (e) {
    catchCode;
} finally {
    finallyCode;
}
...
</code></pre>
<p>If either <code>tryCode</code>, <code>catchCode</code>, or <code>finallyCode</code> contain an <code>await</code>, you need to convert the entire statement into a promise.</p>
<p>If <code>tryCode</code>, <code>catchCode</code>, and <code>finallyCode</code> all contain <code>await</code>s, the entire statement converts into:</p>
<pre class="lang-js prettyprint-override"><code>new Promise((resolveTheTry, rejectTheTry) =&gt; {
    // In general, you should be careful duplicating code when desugaring
    // because it can lead to exponential blowup (imagine what happens if you
    // put finallyCode in the desugared output twice, and then the user writes
    // 32 nested try/finally statements. Language users write that kind of 
    // garbage, then if you're not careful, your interpreter will desugar it
    // into billions or more syntax nodes.
    const finally = (rethrown, e) =&gt; {
        finallyCode;
        if (!rethrown) {
            resolveTheTry();
        } else {
            rejectTryTry(e);
        }
    };
    tryCode.then(() =&gt; finally(false)).catch(e =&gt; {
        catchCode.then(() =&gt; finally(false)).catch(e2 =&gt; finally(true, e2))
    })
}
</code></pre>
<p>If any of <code>tryCode</code>, <code>catchCode</code>, or <code>finallyCode</code> don't contain an <code>await</code>, you can those into <code>new Promise(resolve =&gt; { code; resolve() })</code>. There may be optimized variants, but since there are 6 others, I'm not going to compute them all.</p>
<p>A block without a <code>catch</code> case</p>
<pre class="lang-js prettyprint-override"><code>try {
    tryCode;
} finally {
    finallyCode;
}
</code></pre>
<p>converts into</p>
<pre class="lang-js prettyprint-override"><code>new Promise((resolveTheTry, rejectTheTry) =&gt; {
    const finally = (rethrown, e) =&gt; {
        finallyCode;
        if (!rethrown) {
            resolveTheTry();
        } else {
            rejectTheTry(e);
        }
    };
    tryCode.then(() =&gt; finally(false)).catch(e =&gt; finally(true, e))
});
</code></pre>
<h3>Convert a statement or expression without <code>await</code> into one with <code>await</code></h3>
<p>If necessary, you can convert an expression which doesn't contain <code>await</code></p>
<pre class="lang-js prettyprint-override"><code>expr
</code></pre>
<p>into one that does</p>
<pre class="lang-js prettyprint-override"><code>await new Promise(resolve =&gt; resolve(expr))
</code></pre>
<p>Or a statement</p>
<pre class="lang-js prettyprint-override"><code>stmt;
</code></pre>
<p>into</p>
<pre class="lang-js prettyprint-override"><code>await new Promise(resolve =&gt; { stmt; resolve() });
</code></pre>
<p>Out of the above rules, this is only necessary in the try/catch/finally one.</p>

CC BY-SA 4.0
2023-12-22T01:17:01.847Z
2023-12-22T01:17:01.847Z
tarzh
CC BY-SA 4.0
2023-12-22T01:46:29.113Z
Thanks for your reply, I am able to get the promise structure to work (resolve reject, then, catch) etc, but I'm not sure how this would work on the C# backend, as the entire interpreter doesn't use await at all, but if I wanted to run a function such as a http get request, I'm not sure how I'd make this work with the native functions.
InvaderIzzy
CC BY-SA 4.0
2023-12-22T01:53:21.823Z
I edited my question to show what I have working.
InvaderIzzy
CC BY-SA 4.0
2023-12-22T02:08:04.597Z
Does the native http `request` function take a callback or return a promise? If it does, your `request` wrapper should return a promise object which gets resolved within the callback. If the native function blocks, a non-ideal solution is just to spawn a separate thread or use a thread pool, call the native function, and then call back into the main thread and resolve the promise there.
tarzh
CC BY-SA 4.0
2023-12-22T02:19:23.757Z
I also don't know how you've implemented promises, and whether you have your own async runner or you are using C#'s. If you made your own async runner (you implemented promises without using C#'s async), then you need to integrate it some way with C#'s async runner to call its async functions, and I'm not quite sure how to do that.
tarzh
CC BY-SA 4.0
2023-12-22T02:20:50.497Z
I'm not sure, to call it in C# the only way is to `HttpResponse resp = await HttpClient.GetAsync(url);`
InvaderIzzy
CC BY-SA 4.0
2023-12-22T02:25:33.433Z
Since you're using C# async native functions, the wrappers to those native functions like http `request` will be async or return a C# promise ([`Task`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1?view=net-8.0)). Your inner interpreter needs to be async, but you can wrap it in a function which just calls it and then `ContinueAndWait()` (since your interpreter is the only promise running, and it contains all the promises generated by your code, you can block on it). Then the parser and REPL don't need to be async, even if you're interpreting code as you parse.
tarzh
# Async Implementation
Posted by: username_0 on 2023-12-21
I have recently been making my own programming language and I am finding most concepts easy to understand, however I'm not sure how to implement async/await in my language.
ATM, I'm using C# to create this language and I have no idea how to go about implementing such features.
The language is an interpreted one.
I'm able to implement a JS-like Promise system, but this doesn't help with running asynchronous C# functions, for example sending a HTTP request.
What I have working with such "promise-like" system is this:
var any f = Future.create(|resolve, reject| => resolve(2));
f.then(val => Console.writeLine(val));

It works perfectly within the language, however trying to run async C# functions whilst the entire interpreter does not use async at all I have no idea how to do.
The only options I can think of is

Make every C# function async (like the parser, interpreter, etc.) by default, but I'm not sure if this would help
Use the .GetAwaitor().GetResult() but apparently this shouldn't really be used

I've tried searching and looking everywhere on Google for ways to implement this but I could never find any, so any resources for this topic would be appreciated.
**Tags:** language-design implementation async c#
> **Comments:**
> **username_1**: I take it you're writing an interpreter? It would help to make this explicit. By the way, welcome to the site!  
> **username_0**: @kaya3 thanks, I've edited my question  
---
## 1 answers
### Answer by username_2 on 2023-12-22. Score: 15 | Accepted answer
I'm able to implement a JS-like Promise system, but this doesn't help with running asynchronous C# functions, for example sending a HTTP request.

Async/await is syntax sugar for promises.
You'll need to familiarize yourself with Continuation Passing Style (CPS). Basically, CPS is a way to write regular, sequential ("procedural") code as a chain of callbacks. Ex:
function() {
    let x = foo("a", "b")
    let y = bar("c", "d")
    let z = baz(1, 2, x)
    return y + z
}

converted into CPS is
function(k) {
    foo("a", "b", function(x) {
        bar("c", "d", function(y) {
           baz(1, 2, x, function(z) k(y + z))
        })
    })
}

This is roughly what your interpreter needs to do with async functions before you interpret them. Convert:
async function() {
    let x = await foo("a", "b");
    let y = await bar("c", "d");
    let z = await baz(1, 2, x);
    return y + z;
}

into
function() { return new Promise(resolve => 
    foo("a", "b").then(function(x) {
        bar("c", "d").then(function(y) {
           baz(1, 2, x).then(function(z) resolve(y + z))
        })
    })
}

Specifically:

After parsing an async function or other kind of aysnc block, desugar it into a regular function which returns a promise, where all the awaits are replaced with .then and .catch callbacks.
If your language supports top-level async, you can parse and desugar the entire source to get one big promise which you then execute. Alternatively, to avoid parsing the entire file at once, you can parse and desugar each individual statement into a promise, waiting to parse the next statement until the promise resolves. Basically, write an async function evalRemaining(int pos), which lexes/parses and desugars the first statement into a promise, then adds a .then callback to that promise which recursively calls evalRemaining with the position right after that statement's end.
If your language supports top-level async in a REPL you handle it similarly: read/lex/parse/desugar the current line into a promise, then wait until that promise resolves before asking the user for the next line, by putting the code to do so in the promise callback.

Now, your goal is to desugar a statement or expression with awaits, into a promise expression where the awaits are replaced with .then and .catch callbacks, and the promise resolves into the expression result. This is essentially the same as converting a block of procedural code into a function written in CPS.

There's a lot of existing literature on continuation passing style. You mentioned having trouble finding resources to implement async/await, but if you can rephrase your queries about implementing async/await into implementing CPS conversion, I guarantee you'll find online guides and other resources.
Rule-based async/await (CPS-style) conversion via preorder traversal
Like type-checking, computing the derivative in calculus, and other algorithms, one way to do async/await conversion is to create a fixed set of rules which handle general cases and compose into whatever code you may encounter. Specifically, create a rule for every kind of statement which desugars in a special way, and then to desugar an arbitrary statement, run a preorder traversal, processing each sub-statement or expression with the applicable rule.
Here are some of these rules. If your language has unique semantics, you may need to come up with other rules or modify these rules on your own. There are also be optimized rules for more specific cases which will generate faster / smaller code. Lastly, I didn't check these rules, so there may be bugs:
Async function/block
async function f(...) -> T {
    ...
    <... throw x; ...>
    <... return y; ...>
}

converts into
function f(...) -> Promise {
    return new Promise((resolve, reject) => {
        ...
        <... resolve(x); ...>
        <... reject(y); ...>
        // In case the function doesn't return anything,
        // add this to the end of the Promise or it'll never resolve
        resolve()
    });
}

(e.g. return foo gets converted into resolve(foo), throw bar gets converted into reject(bar), otherwise code is desugared with the following rules and placed into the promise closure).
Async blocks, if your language supports them (like Rust), are similar:
async {
    ...
    result
}

converts into
Promise::new(|resolve| {
    ...
    resolve(result)
})

Desugar from inside out (preorder traversal)
If a statement contains a nested await but it's in an async function or block, desugar the inner async function/block before desugaring the outer one. This also applies to async expressions and other rules.
Statements or expressions without await
A sequence of statements without an await is unchanged.
async function() {
    a;
    while (b) { c; d; }
    try { e; } catch { f; } finally { g; }
}

converts into
function() -> Promise<()> {
    return new Promise(resolve => {
        a;
        while (b) { c; d; }
        try { e; } catch { f; } finally { g; }
        resolve();
    });
}

Expression or expression statement
before;
context <... await expr ...> context;
after;

becomes
before;
expr.then(e => {
    context <... e ...> context;
    after
});

e.g.
let a = foo();
let b = bar(await baz()) * 2;
let c = a + b;

becomes
let a = foo();
baz().then(e => {
   let b = e * 2;
   let c = a + b;
});

If statement
if (cond) {
    then;
} else {
    else;
}
rest;

If either then or else contain an await, the entire if must be converted into a new promise
new Promise(resolveTheIf => {
    if (cond) {
        then.then(resolveTheIf);
    } else {
        else.then(resolveTheIf);
    }
}).then(() => {
    rest;
})

(of course, make sure resolveTheIf doesn't shadow the outer resolve in case then or else contains a return statement).
If cond contains an await, you can convert into the equivalent:
let c = cond;
if (c) {
    then...
} else {
    else...
}
rest...

and then convert from there.
If either then or else does not contain an await, replace then.then(resolveTheIf) or else.then(resolveTheIf) with then; resolveTheIf() or else; resolveTheIf() (respectively).
If your if/else returns an expression like Rust, convert the zero argument call/closure into a one-argument one. Ex:
let foo = if (cond) {
    await then()
} else {
    else()
}
...

converts into
Promise::new(|resolveTheIf| {
    if (cond) {
        then.then(resolveTheIf);
    } else {
        resolveTheIf(else)
    }
}).then(|foo| {
    ...
})

While statement
Similar to the above
while (cond) {
    body;
}
rest;

if body contains an `await, this converts into
const recurse = resolveTheWhile => {
    if (cond) {
        body.then(() => recurse(resolveTheWhile));
    } else {
        resolveTheWhile();
    }
};
new Promise(recurse)

If cond contains an await, you can resolve it before the while block.
Like return and throw when desugaring an async function, break desugars into resolveTheWhile(). If you have nested whiles and allow labeling them, so that users can call break outer from within the inner loop to break the outer one, you have to call resolveTheWhile of outer to break the outer loop.
Switch and for statements
These can be deduced from the if and while statements. I'll show an example of a complicated for statement.
for (let i = await init(); await cond(i); await update(i)) {
    await body;
}
rest;

converts into
init().then(i => {
    const recurse = resolveTheFor => {
        cond(i).then(c => {
            if (c) {
                body.then(() => update(i).then(() => recurse(resolveTheFor)));
            } else {
                resolveTheFor();
            }
        })
    };
}

Try/catch/finally statement
This is similar to the if statement, except that the catch block corresponds to a .catch promise callback.
try {
    tryCode;
} catch (e) {
    catchCode;
} finally {
    finallyCode;
}
...

If either tryCode, catchCode, or finallyCode contain an await, you need to convert the entire statement into a promise.
If tryCode, catchCode, and finallyCode all contain awaits, the entire statement converts into:
new Promise((resolveTheTry, rejectTheTry) => {
    // In general, you should be careful duplicating code when desugaring
    // because it can lead to exponential blowup (imagine what happens if you
    // put finallyCode in the desugared output twice, and then the user writes
    // 32 nested try/finally statements. Language users write that kind of 
    // garbage, then if you're not careful, your interpreter will desugar it
    // into billions or more syntax nodes.
    const finally = (rethrown, e) => {
        finallyCode;
        if (!rethrown) {
            resolveTheTry();
        } else {
            rejectTryTry(e);
        }
    };
    tryCode.then(() => finally(false)).catch(e => {
        catchCode.then(() => finally(false)).catch(e2 => finally(true, e2))
    })
}

If any of tryCode, catchCode, or finallyCode don't contain an await, you can those into new Promise(resolve => { code; resolve() }). There may be optimized variants, but since there are 6 others, I'm not going to compute them all.
A block without a catch case
try {
    tryCode;
} finally {
    finallyCode;
}

converts into
new Promise((resolveTheTry, rejectTheTry) => {
    const finally = (rethrown, e) => {
        finallyCode;
        if (!rethrown) {
            resolveTheTry();
        } else {
            rejectTheTry(e);
        }
    };
    tryCode.then(() => finally(false)).catch(e => finally(true, e))
});

Convert a statement or expression without await into one with await
If necessary, you can convert an expression which doesn't contain await
expr

into one that does
await new Promise(resolve => resolve(expr))

Or a statement
stmt;

into
await new Promise(resolve => { stmt; resolve() });

Out of the above rules, this is only necessary in the try/catch/finally one.
> **Comments:**
> **username_0** (2023-12-22): Thanks for your reply, I am able to get the promise structure to work (resolve reject, then, catch) etc, but I'm not sure how this would work on the C# backend, as the entire interpreter doesn't use await at all, but if I wanted to run a function such as a http get request, I'm not sure how I'd make this work with the native functions.  
> **username_0** (2023-12-22): I edited my question to show what I have working.  
> **username_2** (2023-12-22): Does the native http `request` function take a callback or return a promise? If it does, your `request` wrapper should return a promise object which gets resolved within the callback. If the native function blocks, a non-ideal solution is just to spawn a separate thread or use a thread pool, call the native function, and then call back into the main thread and resolve the promise there.  
> **username_2** (2023-12-22): I also don't know how you've implemented promises, and whether you have your own async runner or you are using C#'s. If you made your own async runner (you implemented promises without using C#'s async), then you need to integrate it some way with C#'s async runner to call its async functions, and I'm not quite sure how to do that.  
> **username_0** (2023-12-22): I'm not sure, to call it in C# the only way is to `HttpResponse resp = await HttpClient.GetAsync(url);`  
> **username_2** (2023-12-22): Since you're using C# async native functions, the wrappers to those native functions like http `request` will be async or return a C# promise ([`Task`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1?view=net-8.0)). Your inner interpreter needs to be async, but you can wrap it in a function which just calls it and then `ContinueAndWait()` (since your interpreter is the only promise running, and it contains all the promises generated by your code, you can block on it). Then the parser and REPL don't need to be async, even if you're interpreting code as you parse.  
---
