Chapters 4 & 5 - Callbacks, Promises, and Async/Await #25
Replies: 1 comment
{{title}}
{{editor}}'s edit
{{editor}}'s edit
-
Hey @Jack-Barry! Great conversation. Thanks for starting it and for highlighting some of the parts of the book that you found most interesting so far. You ask a few open questions, but I'll attempt to provide some answers with no presumption of being comprehensive. How do you double-check for race conditions in your asynchronous code?This is an interesting one and I think the idea of the event loop makes it generally harder to have race conditions (compared to a more traditional thread-based approach to concurrency). UPDATE: We did publish a dedicate article on this topic: I think you can still end up having logical issues that we might define as race conditions though. Just to provide a more concrete example, I really like this answer on stack overflow that defines possible Node.js race conditions as:
I am going to copy (and modify slightly) the example provided there, because I think it's quite representative: const delay = (m) => new Promise(resolve => setTimeout(resolve, m))
let accountBalance = 0
async function getAccountBalance () {
// simulates random delay to retrieve data from a remote source (e.g. a DB)
await delay(Math.random() * 100)
return accountBalance
};
async function setAccountBalance (value) {
// simulates random delay to retrieve data from a remote source (e.g. a DB)
await delay(Math.random() * 100)
accountBalance = value
};
function increment (value, incr) {
return value + incr
};
async function add$50 (label) {
const balance = await getAccountBalance()
console.log(`${label}: get balance`)
const newBalance = increment(balance, 50)
await setAccountBalance(newBalance)
console.log(`${label}: set balance`)
};
async function main () {
const transaction1 = add$50('transaction1') // NOTE no `await`
const transaction2 = add$50('transaction2') // NOTE no `await`
await transaction1 // awaiting here does not stop `transaction2` from being scheduled before transaction 1 is completed
await transaction2
const balance = await getAccountBalance()
console.log(`$${balance}`)
};
main() This example can print either Case 1Complete sequential transactions always prints
Case 2Interleaved transactions always print
This is because the last transaction to update the value is using a stale balance and therefore it's not seeing the latest update. Possible solutionsA simple way to fix this is to await async function main () {
await add$50('transaction1')
await add$50('transaction2')
const balance = await getAccountBalance()
console.log(`$${balance}`)
}; This will effectively serialize the 2 operations solving potential race conditions. In cases where you cannot easily serialize the access, maybe the code is a lot more complicated and the sequence of events depends on a chain of asynchronous events, you might want to implement a solution similar to a mutex like the one described in the stack overflow answer. AFAIK, race conditions are hard in any language and there is no magic bullet. This nice article: Fantastic Bugs and How to Resolve Them Ep2: Race Conditions provides some other interesting examples and ideas on how you might want to spot and troubleshoot race conditions. IMHO, when using JavaScript and Node.js, having a solid understanding of how async works and leveraging the Is there any kind of rule of thumb to determine how many tasks you should allow your program to run concurrently?This is an interesting one and I am not sure I have a generic formula to use to determine what's the upper bound. The upper bound is generally NOT determined not by Node.js itself but by some underlying system resource (e.g. number of open sockets), so different use cases (or even different systems) might end up having different limits. Few interesting resources that IMHO can help with providing more context to this question are the following:
Is there a way to check for conditions that can create a memory leak in an asynchronous function?This is a great question and there are a good number of articles out there that provide very good examples and approaches on how to spot and solve memory leaks. These are some of my favourites: |
Beta Was this translation helpful? Give feedback.
{{title}}
-
When I first started programming in Node,
Promise
was already widely adopted andasync
/await
was just starting to gain widespread use. Coming from primarily coding in Ruby, this was a very confusing idea to me, as I didn't even yet understand what "asynchronous" was, or why it was such a huge deal to be able to write it in a "synchronous" manner.Now, having gone through the first few chapters of this book and becoming more familiar with how the event loop works and interacting with the continuation passing style which is common in older libraries built with Node, I feel a much better appreciation for what
Promise
andasync
/await
do for us as developers.A few highlights from these chapters that I'd like to offer up for discussion:
return
before a callback is so important to ensure an early exit when errors are encountered. I had seen thereturn callback(err)
syntax before but didn't really understand why you wouldreturn
the callback when an error was encountered vs. not returning it otherwise. (page 96)return process.nextTick(cb)
worked. It was a bit of an "Aha" moment when I realized that it was a way to ensure that the code would not execute in the same cycle, because other operations which rely on a callback to finish would not be executed in the same cycle of the event loop. (page 101)Promise
andasync
/await
significantly improve the ability to handle errors consistently (page 127, 143)await
does not literally halt execution of code until the operation has completed - it's just syntactic sugar that makes it easier to reason about what will be done once the value returned by theasync
function is available. (page 142)leakingLoop
example of unsettledPromise
objects was an interesting problem. Having more understanding of what's going on under the hood can help us avoid writingPromise
andasync
/await
code that behaves in ways like this. (page 153)Questions for Discussion
Beta Was this translation helpful? Give feedback.
All reactions