# Användning av await - asynkron programkod

20 september 2022 | https://www.ribomation.se/blog/2022/anvandning-av-await-asynkron-programkod/

Den här artikeln tar vid direkt efter där [förra veckans artikel](/blog/2022/3-former-av-asynkron-bearbetning-i-javascript-del-2/) slutade. Det vi diskuterade förra vecka var begreppet _promise_ och hur du använder det i JavaScript. Den första artikeln i denna serie diskuterade call-back funktioner och de problem med nästlade funktioner detta för med sig.

Emellertid, var de först med begreppet `await`, som asynkron programkod kunde formuleras på ett både enkelt och begripligt sätt. JavaScript fick stöd för _async funktioner_ med await i ES2017. Funktioner med await var så pass efterlängtat att det tog bara några månader förrän detta var implementerat i alla moderna webbläsare. I och med att både Google Chrome och Node.js bygger V8 (JS Engine), så blev det tillgängligt för programkod på serversidan också.

Hur använder man då `await`?
----------------------------

I stället för att anropa `.then()` på ett promise objekt, skriver man await framför i stället.

**Kodexempel med promise objekt**

```js
fetch('https://www.ribomation.se/')
    .then(response => response.text())
    .then(html => console.log(html));
```

**Kodexempel med `await`**

```js
const response = await fetch('https://www.ribomation.se/');
const html     = await response.text();
console.log(html);
```

I koden ovan, gör vi ett HTTP anrop med `fetch()`, som returnerar ett promise objekt, vilket vi sen får ett nytt promise objekt av via funktionen `.text()` som returnerar själva innehållet (eng. _response body_).

Async funktioner
----------------

Inledningsvis, var man tvungen att använda await i en funktion markerad som `async` och sen anropa denna.

```js
async function main() {
    const response = await fetch('https://www.ribomation.se/');
    const html     = await response.text();
    console.log(html);
}

main();
```

Eller, allt i ett svep som en async IIFE (_Immediately Invoked Function Expression_).

```js
(async () => {
    const response = await fetch('https://www.ribomation.se/');
    const html     = await response.text();
    console.log(html);
})();
```

Några år senare, med ES2020, kom _top-level await_ som medgav att man kunde anropa await direkt i huvudprogrammet, såsom det första kodexemplet med await illustrerade.

Historik
--------

JavaScript var ingalunda först med att införa await som ett sätt att sekventiellt ordna anrop till continuation funktioner. Själva konceptet dök upp i det funktionella språket ML i början på 1990-talet med implementerades och blev populärt i det senare funktionella språket Haskel i slutet av 1990-talet.

När Microsoft implementerade sitt eget funktionella språk F# en bit in på 2000-talet, baserat primärt på ML med viss mix från Haskel, så blev det en del av vad de kallade _asynchronous workflows_. Från F#, blev steget till C# kort och async/await infördes där av Anders Hejlsberg 2012. I och med att begreppet diskuterades vid den tiden för ES, så var Anders kvick med att implementera det i TypeScript också några år senare.

I C++ har await helt nyligen dykt upp i och med standarden C++20 börjar bli implementerad. Det utgör en del av stödet för coroutines och representeras av det nya reserverade order `co_await`.

```cpp
auto my_coroutine(std::string url) -> Task<std::string> {
    auto response = co_await fetch_cxx(url);
    auto text     = co_await response.extract_body_as_text();
    std::cout << text << std::endl;
}

int main() {
    std::string url = "https://www.ribomation.se/"s;
    auto task = my_coroutine( std::move(url) );
}
```

Hantering av fel
----------------

I förra veckan, såg vi hur man hanterade fel från promise objekt, via kaskadfunktionen `.catch()`. Med await blir det betydligt enklare och mer begripligt.

Om vi börjar med hur det ser ut när man använder ett promise objekt.

```js
fetch('https://www.ribomation.se/')
    .then(response => response.text())
    .then(html => console.log(html))
    .catch(err => console.error(err));
```

Går vi sen över till användning av await, så blir det enklare med den familjära `try-catch` satsen (vilken Bjarne Stroustrup uppfann i slutet på 1980-talet för att kunna kapsla in setjmp/longjmp anrop när han införde exceptions i språket).

```js
try {
    const response = await fetch('https://www.ribomation.se/');
    const html     = await response.text();
    console.log(html);
} catch (err) {
    console.error(err);
}
```

Kopiera en fil
--------------

I min [första artikel](/blog/2022/3-former-av-asynkron-bearbetning-i-javascript/) i denna serie, illustrerade jag begreppet _call-back hell_ med en funktion som i Node.js kopierar innehållet i en textfil till en ny fil och transformerar texten till versaler. Här kommer den funktionen på nytt, så vi kan följa evolutionsstegen.

```js
function copyfile(fromFile, toFile) {
    open(fromFile, 'r', (err, fd) => {
       if (err) die(err);
       fstat(fd, (err, stats) => {
           if (err) die(err);
           const storage = Buffer.alloc(stats.size);
           read(fd, storage,0, storage.length,null, (err, bytesIn, data) => {
               if (err) die(err);
               console.log('Read  %d bytes from %s', bytesIn, basename(fromFile));
               close(fd, (err) => {
                   if (err) die(err);
                   open(toFile, 'w', (err, fd) => {
                       if (err) die(err);
                       const payload = data.toString().toUpperCase();
                       write(fd, payload, (err, bytesOut) => {
                           if (err) die(err);
                           close(fd, (err) => {
                               console.log('Wrote %d bytes to %s', bytesOut, basename(toFile));
                           });
                       });
                   });
               });
           });
       })
    });
}
```

I min [andra artikel](/blog/2022/3-former-av-asynkron-bearbetning-i-javascript-del-2/), skrev jag om funktionen till att använda en _promise-kedja_ på följande vis.

```js
function copyfile(fromFile, toFile) {
    const state = {};
    return open(fromFile, 'r')
        .then(fd => {
            state.fd = fd;
            return fstat(fd);
        })
        .then(stats => {
            const storage = Buffer.alloc(stats.size);
            return read(state.fd, storage, 0, storage.length, null);
        })
        .then(response => {
            console.log('Read  %d bytes from %s', response.bytesRead, basename(fromFile));
            state.text = response.buffer.toString();
            return close(state.fd);
        })
        .then(() => {
            return open(toFile, 'w');
        })
        .then(fd => {
            state.fd = fd;
            const payload = state.text.toString().toUpperCase();
            return write(fd, payload);
        })
        .then(response => {
            state.bytesWritten = response.bytesWritten;
            return close(state.fd);
        })
        .then(() => {
            console.log('Wrote %d bytes to %s', state.bytesWritten, basename(toFile));
        })
        .catch(err => {
            console.error('*** %s', err);
        });
}
```

Till slut, kan vi nu skriva om funktionen till att vara async. Notera nedan, hur vi jag använder `try-catch-finally`.

```js
async function copyfile(fromFile, toFile) {
    console.time('Elapsed');
    try {
        let   fd       = await open(fromFile, 'r');
        const stats    = await fstat(fd);
        const storage  = Buffer.alloc(stats.size);
        let   response = await read(fd, storage, 0, storage.length, null);
        await close(fd);
        console.log('Read  %d bytes from %s', response.bytesRead, basename(fromFile));
        
        const text    = response.buffer.toString();
        const payload = text.toString().toUpperCase();
        fd            = await open(toFile, 'w');
        response      = await write(fd, payload);
        await close(fd);
        console.log('Wrote %d bytes to %s', response.bytesWritten, basename(toFile));
    } catch (err) {
        console.error('failed: %o', err)
    } finally {
        console.timeEnd('Elapsed')
    }
}
```

Hela programmet ser ut så här:

```js
import {fileURLToPath} from 'node:url';
import {basename, dirname, extname, join} from 'node:path';
import fs from 'node:fs';
import {promisify} from 'node:util'

const open  = promisify(fs.open);
const fstat = promisify(fs.fstat);
const read  = promisify(fs.read);
const write = promisify(fs.write);
const close = promisify(fs.close);

async function copyfile(fromFile, toFile) { /*...*/ }

const scriptFilePath = process.argv[2] || fileURLToPath(import.meta.url);
const scriptFileName = basename(scriptFilePath, extname(scriptFilePath));
const outputFilePath = join(dirname(scriptFilePath), scriptFileName + '-copy.txt');

await copyfile(scriptFilePath, outputFilePath);
```

Samt, ett körexempel här:

```shell
$ node copyfile-await.mjs 
Read  1480 bytes from copyfile-await.mjs
Wrote 1480 bytes to copyfile-await-copy.txt
Elapsed: 10.889ms
```
