javascript - Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference -
given following examples, why outerscopevar
undefined in cases?
var outerscopevar; var img = document.createelement('img'); img.onload = function() { outerscopevar = this.width; }; img.src = 'lolcat.png'; alert(outerscopevar);
var outerscopevar; settimeout(function() { outerscopevar = 'hello asynchronous world!'; }, 0); alert(outerscopevar);
// example using jquery var outerscopevar; $.post('loldog', function(response) { outerscopevar = response; }); alert(outerscopevar);
// node.js example var outerscopevar; fs.readfile('./catdog.html', function(err, data) { outerscopevar = data; }); console.log(outerscopevar);
// promises var outerscopevar; mypromise.then(function (response) { outerscopevar = response; }); console.log(outerscopevar);
// geolocation api var outerscopevar; navigator.geolocation.getcurrentposition(function (pos) { outerscopevar = pos; }); console.log(outerscopevar);
why output undefined
in of these examples? don't want workarounds, want know why happening.
note: canonical question javascript asynchronicity. feel free improve question , add more simplified examples community can identify with.
one word answer: asynchronicity.
forewords
this topic has been iterated @ least couple of thousands times, here, in stack overflow. hence, first off i'd point out extremely useful resources:
@felix kling's "how return response ajax call". see excellent answer explaining synchronous , asynchronous flows, "restructure code" section.
@benjamin gruenbaum has put lot of effort explaining asynchronicity in same thread.@matt esch's answer "get data fs.readfile" explains asynchronicity extremely in simple manner.
the answer question @ hand
let's trace common behavior first. in examples, outerscopevar
modified inside of function. function not executed immediately, being assigned or passed argument. call callback.
now question is, when callback called?
it depends on case. let's try trace common behavior again:
img.onload
may called sometime in future, when (and if) image has loaded.settimeout
may called sometime in future, after delay has expired , timeout hasn't been cancelledcleartimeout
. note: when using0
delay, browsers have minimum timeout delay cap (specified 4ms in html5 spec).- jquery
$.post
's callback may called sometime in future, when (and if) ajax request has been completed successfully. - node.js's
fs.readfile
may called sometime in future, when file has been read or thrown error.
in cases, have callback may run sometime in future. "sometime in future" refer asynchronous flow.
asynchronous execution pushed out of synchronous flow. is, asynchronous code never execute while synchronous code stack executing. meaning of javascript being single-threaded.
more specifically, when js engine idle -- not executing stack of (a)synchronous code -- poll events may have triggered asynchronous callbacks (e.g. expired timeout, received network response) , execute them 1 after another. regarded event loop.
that is, asynchronous code highlighted in hand-drawn red shapes may execute after remaining synchronous code in respective code blocks have executed:
in short, callback functions created synchronously, executed asynchronously. can't rely on execution of asynchronous function until know has executed, , how that?
it simple, really. logic depends on asynchronous function execution should started/called inside asynchronous function. example, moving alert
s , console.log
s inside callback function output expected result, because result available @ point.
implementing own callback logic
often need more things result asynchronous function, or different things result depending asynchronous function has been called. let's tackle bit more complex example:
var outerscopevar; hellocatasync(); alert(outerscopevar); function hellocatasync() { settimeout(function() { outerscopevar = 'nya'; }, math.random() * 2000); }
note: i'm using settimeout
random delay generic asynchronous function, same example applies ajax, readfile
, onload
, other asynchronous flow.
this example suffers same issue other examples, not waiting until asynchronous function executes.
let's tackle implementing callback system of our own. first off, rid of ugly outerscopevar
useless in case. add parameter accepts function argument, our callback. when asynchronous operation finishes, call callback passing result. implementation (please read comments in order):
// 1. call hellocatasync passing callback function, // called receiving result async operation hellocatasync(function(result) { // 5. received result async function, // whatever want it: alert(result); }); // 2. "callback" parameter reference function // passed argument hellocatasync call function hellocatasync(callback) { // 3. start async operation: settimeout(function() { // 4. finished async operation, // call callback passing result argument callback('nya'); }, math.random() * 2000); }
most in real use cases, dom api , libraries provide callback functionality (the hellocatasync
implementation in demonstrative example). need pass callback function , understand execute out of synchronous flow, , restructure code accommodate that.
you notice due asynchronous nature, impossible return
value asynchronous flow synchronous flow callback defined, asynchronous callbacks executed long after synchronous code has finished executing.
instead of return
ing value asynchronous callback, have make use of callback pattern, or... promises.
promises
although there ways keep callback hell @ bay vanilla js, promises growing in popularity , being standardized in es6 (see promise - mdn).
promises (a.k.a. futures) provide more linear, , pleasant, reading of asynchronous code, explaining entire functionality out of scope of question. instead, i'll leave these excellent resources interested:
more reading material javascript asynchronicity
- the art of node - callbacks explains asynchronous code , callbacks vanilla js examples , node.js code well.
note: i've marked answer community wiki, hence @ least 100 reputation can edit , improve it! please feel free improve answer, or submit new answer if you'd well.
i want turn question canonical topic answer asynchronicity issues unrelated ajax (there how return response ajax call? that), hence topic needs , helpful possible!
Comments
Post a Comment