{
"$type": "site.standard.document",
"content": {
"$type": "at.markpub.markdown",
"text": {
"$type": "at.markpub.text",
"markdown": "If you're anything like me then `async` and `await` were a revelation when they got added to JavaScript. Finally, there was a solution to the endless callbacks and chained `then()`s. It enabled us to write synchronous looking code with all the goodness asynchronicty provides.\n\nBut very quickly you might have noticed an issue handling rejected Promises. To address it you probably tackled it in one of two way:\n\n## Using `try`/`catch` inline is a flow control nightmare\nThe simplest method was to just wrap everything in a try/catch block.\n\n```javascript\ntry {\n const res = await fetch(\"...\");\n const data = await res.json();\n} catch (err) {\n console.log(err);\n}\n```\n\nI found using this method often resulted in me dumping huge amounts of code within the try block. This meant that every error would result in all the code in the try not being run which wasn't always what I would want; sometimes I would want to handle an error by simply logging the error to the console and moving on with the rest of the code.\n\nThis brings to the second method.\n\n## `.catch()` is an exercise in tedium\nWith this method you handle the error handling on a per-Promise-basis by chaining a `catch()`.\n\n```javascript\nconst res = await fetch(\"...\").catch((err) => console.log(err));\n```\n\nThe problem here is a matter of developer experience. It SUCKS to keep writing a `.catch()` at the end of everything. And, honestly, who's got that kind of time?\n\nAnd this brings me to the method that I use: the third method.\n\n## The `call()` utility function is chef's kiss\nWith this method, we go back to our trusty try/catch block, but this time it's in a handy helper function. We are are going to make use of array destructuring to make the error a first-class citizen along with the desired resolution from the Promise.\n\n```javascript\nconst [data, err] = await call(fetch(\"...\"));\nif (err) console.log(err);\n\nasync function call(promise) {\n let data, error;\n try {\n const res = await promise;\n data = await res.json();\n } catch (err) {\n error = err;\n }\n return [data, error];\n}\n```\n\nUsing this method we relegate our Promise rejection and resolution handling to the `call()` function. In this case, I even have it handling the `.json()` for me so I can use `fetch` and immediately get the JSON data I care about. This helper function returns an array where the first item is the data from a resolved Promise and the second item is the err from the rejected Promise. If the Promise resolved, the error will be undefined; if the Promise rejects the data will be undefined.\n\nThis let's me write very clean, synchronous looking code. It make handling error a breeze as I can easily either log the error and move on or provide some complex fallback scenario if I need to.\n\nSince I've been writing all my async/await code this I've found that I've been able to write much more complex applications in less time, with fewer bugs, and with richer error-handling than I did previously."
}
},
"description": "Seriously, this small utility function is a must-have in every project I work on.",
"path": "/posts/safe-async-await-changed-my-life",
"publishedAt": "2020-10-09T00:00:00.000Z",
"site": "at://did:plc:2lm4lab5fhnj6kaazrxopv37/site.standard.publication/self",
"tags": [
"javascript"
],
"title": "This one weird trick changed my whole async/await game"
}