What is Async/Await?
async/await is a new way to write asynchronous code. Previous methods include callback functions and Proise.
async/await is based on Promise, which can not be used for ordinary callback functions.
Like Promise, async/await is non-blocking.
async/await makes asynchronous code look like synchronous code, which is its magic.
Async/Await grammar
Suppose the function getJSON returns a value of Promise, and Promise resolves has some JSON objects. We just want to call it and record the JSON and return to completion.
1) Use Promise:
const makeRequest = () => getJSON().then(data => { console.log(data) return "done" }) makeRequest()
2) Use Async:
const makeRequest = async () => { // await getJSON()Express console.log Will wait getJSON Of promise Success reosolve It will be executed later. console.log(await getJSON) return "done" } makeRequest()
Distinction:
1) There is an additional aync keyword in front of the function. The await keyword can only be used in functions defined by aync. The async function implicitly returns a promise whose reosolve value is the return value of the function. (The reosolve value in the example is the string "done")
2) Point 1 implies that we cannot use await in the outermost code because it is not in the async function. For example:
// Can't be used in the outermost code await await makeRequest() // This is going to happen. makeRequest().then((result) => { // Code })
Why is Async/Await better?
1) Using async functions can make the code much simpler, without requiring some then like Promise, writing anonymous functions to handle Promise's resolve values, defining redundant data variables, and avoiding nested code.
2) Error handling:
Async/Await allows try/catch to handle both synchronous and asynchronous errors. In the promise example below, try / catch cannot handle JSON.parse errors because it is in Promise. We need to use. catch, which makes error handling code very redundant. And, in our actual production code will be more complex.
const makeRequest = () => { try { getJSON().then(result => { // JSON.parse Maybe something goes wrong. const data = JSON.parse(result) console.log(data) }) // Unannotate to handle errors in asynchronous code // .catch((err) => { // console.log(err) // }) } catch (err) { console.log(err) } }
With aync/await, catch can handle JSON.parse errors:
const makeRequest = async () => { try { // this parse may fail const data = JSON.parse(await getJSON()) console.log(data) } catch (err) { console.log(err) } }
3) Conditional statement
Conditional statements are the same as error capture. Conditional statements can also be used in Async as usual.
Promise:
const makeRequest = () => { return getJSON().then(data => { if (data.needsAnotherRequest) { return makeAnotherRequest(data).then(moreData => { console.log(moreData) return moreData }) } else { console.log(data) return data } }) }
Async/Await:
const makeRequest = async () => { const data = await getJSON() if (data.needsAnotherRequest) { const moreData = await makeAnotherRequest(data); console.log(moreData) return moreData } else { console.log(data) return data } }
4) Intermediate value
You've probably encountered a scenario where promise1 is called, promise2 is called using the results returned by promise1, and then promise3 is called using the results of both.
const makeRequest = () => { return promise1().then(value1 => { return promise2(value1).then(value2 => { return promise3(value1, value2) }) }) }
If promise3 does not require value1, nesting will become easier. If you can't stand nesting, you can put value 1 & 2 in Promise.all to avoid deep nesting, but this method sacrifices semantics for readability. There is no other reason to put value1 and value2 in an array than to avoid nesting.
const makeRequest = () => { return promise1().then(value1 => { return Promise.all([value1, promise2(value1)]) }).then(([value1, value2]) => { return promise3(value1, value2) }) }
With async/await, the code becomes extremely simple and intuitive.
const makeRequest = async () => { const value1 = await promise1() const value2 = await promise2(value1) return promise3(value1, value2) }
5) Error stack
If Promise is called continuously, handling errors is cumbersome. You can't tell where the mistake is.
const makeRequest = () => { return callAPromise() .then(() => callAPromise()) .then(() => callAPromise()) .then(() => callAPromise()) .then(() => callAPromise()) .then(() => { throw new Error("oops"); }) } makeRequest().catch(err => { console.log(err); // output // Error: oops at callAPromise.then.then.then.then.then (index.js:8:13) })
The error stack in async/await points to the function where the error is located. In the development environment, this advantage is not great. However, it will be very useful when you analyze the error log of the production environment. At this point, it is better to know that the error occurred in makeRequest than in the then chain.
const makeRequest = async () => { await callAPromise() await callAPromise() await callAPromise() await callAPromise() await callAPromise() throw new Error("oops"); } makeRequest().catch(err => { console.log(err); // output // Error: oops at makeRequest (index.js:7:9) })
6) Debugging
async/await can make code debugging easier. Two reasons make debugging Promise painful:
1 cannot set breakpoints in arrow functions that return expressions
2) If you set a breakpoint in the. then block and use the Step Over shortcut, the debugger will not jump to the next. then, because it will only skip the asynchronous code.
With await/async, you no longer need so many arrow functions, so you can skip the await statement just like debugging synchronous code.