11

The general perception is that JavaScript is intrinsically single-threaded but it can run asynchronously. I wonder how a single-threaded model like this handles AJAX requests that are non-blocking?

Lets say a non-blocking AJAX request is fired in a browser, but doesn't get a response immediately. If the event loop keeps checking for the response, doesn't the execution get blocked? Does the event loop keeps checking for its status and 're-adding' the task to the back of the macrotask queue when there is no response?

From what I understand, Node.js does silently spawn threads to handle I/O operations accessing disks, databases, network sockets etc. Does JavaScript in browsers spawn threads to handle AJAX too?

A similar question could be asked about the following:

var img = new Image();
img.onerror=function(){alert('error: '+this.src);}
img.onload=function(){alert('image loaded: '+this.src);}
img.src='path/to/image.jpg';

Does the last line of code above causes an additional thread to be spawned, because the statement seems to be non-blocking?

Chong Lip Phang
  • 8,755
  • 5
  • 65
  • 100

1 Answers1

12

The general perception is that JavaScript is intrinsically single-threaded but it can run asynchronously.

It's true that JavaScript is specified¹ such that only a single thread can be executing within a realm at any given time. (A realm is the global environment and its associated objects; e.g. a window/tab [on browsers].) You can have multiple active threads (in different windows, or via web workers), and they can talk to each other (via postMessage) or even share some memory (SharedArrayBuffer), but they can't access the same realm at the same time. Keeping realms effectively single-threaded avoids a huge range of concurrent programming pitfalls.

I wonder how a single-threaded model like this handles AJAX requests that are non-blocking?

JavaScript allowing only one thread within the JavaScript environment at a time doesn't mean that the host (the browser) is single-threaded. An asynchronous ajax request is handed off to the browser's network handling.

JavaScript works on the basis of a job queue (the HTML5 speec calls it a task queue, but the JavaScript spec speaks of "jobs" — it's just a name). The JavaScript thread picks up a job from the queue, runs that job to completion, and then picks up the next job, if any. (It's a bit more complicated than that, but that's the basic idea.) While the thread is running a job, no other thread can run another job in the same realm.

So when an ajax request completes (success, timeout, whatever), the browser (on a non-JavaScript thread, probably) puts a job in the JavaScript job queue to call the ajax callback. The JavaScript thread picks up that job and calls the callback.

It's worth noting that that is also exactly what it does in response to other things that happen, such as the user clicking something.

Lets say a non-blocking AJAX request is fired in a browser, but doesn't get a response immediately. If the event loop keeps checking for the response, doesn't the execution get blocked?

The key is that the thread doesn't continually check back for a response. The thread just watches the job queue. The browser's network handler handles completion of network requests.


¹ This was made explicit in ES2015, but it was the case for common environments (browsers, Node.js) for years prior to that. There were JavaScript environments that allowed multiple threads in a realm (like Rhino, running JavaScript on the Java VM), but they weren't considered important enough to prevent ES2015 adding this requirement, and doing so allowed defining precise semantics around several new features that would have been much more complicated to specify, if it was even possible, while remaining silent on the subject of threading.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • ok...So are you saying there are two separate threads, one for the JavaScript, and one for the browser? – Chong Lip Phang Jul 31 '17 at 12:15
  • @ChongLipPhang: I'm not saying specifically *two*, no. But the browser will have more than just the one thread it uses to run JavaScript code. I think while you **could** make a browser with just one thread in total, it would likely be a browser with very poor user experience. It's going to have **at least** two threads, and probably more. It'll have one for JavaScript (typically one per window/tab for JavaScript, actually), and at least one other for handling things like I/O completions, etc. – T.J. Crowder Jul 31 '17 at 12:19
  • So you mean browser network handler runs a different thread and when that request is complete, the handler puts the callback in the queue. Right ? So all browser web apis like settimeout, setinterval they also run on different threads ? – Piyush Jun 16 '19 at 08:11
  • @Piyush - They *might* run on different threads, or they might be implemented differently. The main JavaScript thread is allowed to do non-JavaScript work between jobs, and on browsers it does (it paints the UI). Browsers might implement `setTimeout` and such by having the thread check (between jobs) for timers that are due to fire. (I think current ones do it differently from that, but...) For anything more complex (like ajax), it's likely they use separate threads. – T.J. Crowder Jun 16 '19 at 09:29