diff --git a/1-js/11-async/07-microtask-queue/article.md b/1-js/11-async/07-microtask-queue/article.md index 3f6011c4..dbd0ab0e 100644 --- a/1-js/11-async/07-microtask-queue/article.md +++ b/1-js/11-async/07-microtask-queue/article.md @@ -38,11 +38,9 @@ That's why "code finished" in the example above shows first. Promise handlers always go through that internal queue. -If there's a chain with multiple `.then/catch/finally`, then every one of them is executed asynchronously. +If there's a chain with multiple `.then/catch/finally`, then every one of them is executed asynchronously. That is, it first gets queued, and executed when the current code is complete and previously queued handlers are finished. -That is, it first gets queued, and executed when the current code is complete and previously queued handlers are finished. - -What if the order matters for us? How to make `code finished` work after `promise done`? +**What if the order matters for us? How to make `code finished` work after `promise done`?** Easy, just put it into the queue with `.then`: @@ -56,7 +54,7 @@ Now the order is as intended. ## Event loop -Browser Javascript, as well as Node.js, is based on an *event loop*. +In-browser Javascript, as well as Node.js, is based on an *event loop*. "Event loop" is a process when the engine sleeps and waits for events, then reacts on those and sleeps again. @@ -73,7 +71,7 @@ Things happen -- the engine handles them -- and waits for more to happen (while As you can see, there's also a queue here. A so-called "macrotask queue" (v8 term). -When an event happens, and the engine is busy, the event is enqueued. +When an event happens, while the engine is busy, its handling is enqueued. For instance, while the engine is busy processing a network `fetch`, a user may move their mouse causing `mousemove`, and `setTimeout` may be due and so on, just as painted on the picture above. @@ -90,7 +88,7 @@ In other words, the engine first executes all microtasks, and then takes a macro For instance, take a look: ```js run -setTimeout(() => alert("timeout"), 0); +setTimeout(() => alert("timeout")); Promise.resolve() .then(() => alert("promise")); @@ -122,9 +120,7 @@ Promise.resolve() Naturally, `promise` shows up first, because `setTimeout` macrotask awaits in the less-priority macrotask queue. -**As a side effect, macrotasks are handled only when promises give the engine a "free time".** - -So call have a promise chain that doesn't wait for anything, then things like `setTimeout` or event handlers can never get in the middle. +As a logical consequence, macrotasks are handled only when promises give the engine a "free time". So if we have a promise chain that doesn't wait for anything, then things like `setTimeout` or event handlers can never get in the middle. ## Unhandled rejection @@ -159,12 +155,12 @@ promise.catch(err => alert('caught')); window.addEventListener('unhandledrejection', event => alert(event.reason)); ``` -Now let's say, we'll be catching the error, but after an extremely small delay: +Now let's say, we'll be catching the error, but after `setTimeout`: ```js run let promise = Promise.reject(new Error("Promise Failed!")); *!* -setTimeout(() => promise.catch(err => alert('caught')), 0); +setTimeout(() => promise.catch(err => alert('caught'))); */!* // Error: Promise Failed! @@ -173,7 +169,7 @@ window.addEventListener('unhandledrejection', event => alert(event.reason)); Now the unhandled rejction appears again. Why? Because `unhandledrejection` triggers when the microtask queue is complete. The engine examines promises and, if any of them is in "rejected" state, then the event is generated. -In the example above `setTimeout` adds the `.catch`, and it triggers too, of course it does, but later, after the event has already occured. +In the example, the `.catch` added by `setTimeout` triggers too, of course it does, but later, after `unhandledrejection` has already occured. ## Summary diff --git a/1-js/11-async/08-async-await/article.md b/1-js/11-async/08-async-await/article.md index 122be997..e652e476 100644 --- a/1-js/11-async/08-async-await/article.md +++ b/1-js/11-async/08-async-await/article.md @@ -130,19 +130,30 @@ let response = await fetch('/article/promise-chaining/user.json'); let user = await response.json(); ``` -So we need to have a wrapping async function for the code that awaits. Just as in the example above. -```` -````smart header="`await` accepts thenables" -Like `promise.then`, `await` allows to use thenable objects (those with a callable `then` method). Again, the idea is that a 3rd-party object may not be a promise, but promise-compatible: if it supports `.then`, that's enough to use with `await`. +We can wrap it into an anonymous async function, like this: + +```js run +(async () => { + let response = await fetch('/article/promise-chaining/user.json'); + let user = await response.json(); + ... +})(); +``` + + +```` +````smart header="`await` accepts \"thenables\"" +Like `promise.then`, `await` allows to use thenable objects (those with a callable `then` method). The idea is that a 3rd-party object may not be a promise, but promise-compatible: if it supports `.then`, that's enough to use with `await`. + +Here's a demo `Thenable` class, the `await` below accepts its instances: -For instance, here `await` accepts `new Thenable(1)`: ```js run class Thenable { constructor(num) { this.num = num; } then(resolve, reject) { - alert(resolve); // function() { native code } + alert(resolve); // resolve with this.num*2 after 1000ms setTimeout(() => resolve(this.num * 2), 1000); // (*) } @@ -161,9 +172,7 @@ If `await` gets a non-promise object with `.then`, it calls that method providin ```` ````smart header="Async methods" -A class method can also be async, just put `async` before it. - -Like here: +To declare an async class method, just prepend it with `async`: ```js run class Waiter {