I’m following a course and have tried to break down this question in the code (see comments)
function translate (word) {
const apiKey = '...'
return 'https://www.googleapis.com' +
'/language/translate/v2' +
`?key=${apiKey}` +
'&source=en' +
'&target=fr' +
`&q=${encodeURIComponent(word)}`
}
function sayHello () {
console.log('Hello')
}
function parseData (response) {
return response.json()
}
function logTranslation (parsedResponse) {
console.log(
parsedResponse
.data
.translations[0]
.translatedText // 'Pourquoi'
)
}
function delayScriptRunning () {
for (let i = 0; i < 100000000; i++) {
Math.random()
}
}
// Let's start from here
// browser feature timer set and after ~1ms
// sayHello is added to the callback queue
// where it waits until the callstack is free
window.setTimeout(sayHello, 0)
// xmlHttpRequest request is sent to the given url
// a promise object is created with a
// value property set to undefined
// and an empty onfulfillment array
fetch(translate('why'))
// parseData function definition is immediately added to the onfulfillment array
.then(parseData)
// logTranslation is also immediately added to the onfulfillment array
.then(logTranslation)
// global execution is delayed by a couple of seconds
delayScriptRunning()
// during this delay the translate api sends back a response
// which is assigned to the promise's value property
// with the value being set onfulfillment is triggered and the callbacks
// 'parseData' and 'logTranslation' are added to the microtask queue
// 'Me First!' is logged to the console
console.log('Me first!')
// The callstack is now empty!!
// micro-task queue takes priority over the callback queue
// 'parseData' is added to the callstack, executed and removed on return
// 'logTranslation' is added to the callstack, executed and removed on return
// Finally the callstack is empty and sayHello can be passed to the callstack and executed
I suppose a version without comments might also be helpful
The way that I understand this is: setTimeout that the fetch first takes place, but takes a long time to happen because of network communication delays, and after the delay loop as part of the script execution Me first! is immediately shown.
After that there is no guarantee of which order that things occur in. It can depend on the browser and differing network speeds.
In your case the setTimeout triggered first before the fetch was resolved which makes sense, as it takes a long time (comparatively for computers) for the fetch request to be sent to Google and for the translation to occur, before being sent back to you.
In theory ‘Me first!’ will always come first, as the microtask queue has to wait for the global execution to complete e.g. for the callstack to be empty
I have read on stackoverflow about the network, but in that instance the delay the person was using was only a short for loop of what a few milliseconds.
I meant to add this example where we take the .json() call out of the equation
In this example it does run in a predictable order
‘Me first!’
response object
‘Hello’
So this points to the json call, which I believe is asynchronous. Where my brain starts to melt, is I can picture parseData being added to the callstack, but what happens on it’s return, what happens to logTranslation?
I guess just to complicate things we need to take into account console.log is asynchronous too
The settimeout does trigger almost immediately, but then sayHello has to sit in the callback queue, which essentially has to wait for the global execution to complete and anything sat in the microtask queue in theory
Thanks man, your help is appreciated and I will look into the network queue side of it
You have three promises being set up there. One for fetch, and two for then.
The fetch line results in a promise. The code in that translate function gives a string url. That promise is resolved when the request to that url is resolved, resulting in an HTTP response.
The first .then line sets up a second promise, waiting for the fetch function to be resolved. When it is resolved the HTTP response is sent to the parseData function.
The second .then line sets up a third and final promise, waiting for the second promise to be resolved before passing the result to logTranslation.
I’m currently distracted by a few dozen people around me, but hopefully this helps.
Maybe I’m not understanding you clearly (very possible), but this is kind of at odds with the instructors breakdown of events. In fact he has a serious dislike of the word ‘then’, preferring ‘store-function-we-want-to-autotrigger-on-value-property-being-updated’ — catchy!
In his breakdown all ‘then’ does is push the full function definition in to the onfulfillment array. That’s it job done! ‘then’ does not come into play at a later stage.
I am going to have to go and read up on this Paul, maybe from some other sources.
It appears I am mistaking a fulfilled response with a resolved one
A promise is fulfilled when the HTTP status and the headers are available, and albeit it provides .text and .json methods which have access to the body, it doesn’t guarantee that the body of the response has arrived. So when we do
.then(response => response.json()) // promise returned
.then(parsed => something)
a promise is returned
Thanks for the help and apologies for being so stubbornly dense. lol
Note it is still kind of frustrating that other sources don’t really seem to go into enough depth. No mention of the network queue or the fulfillment array, just a ‘trust me’ approach. more to read…
That tends to be what specifications are for. From the link I provided earlier, the specs on what Fetch actually does should give you enough reading material to work with.