This article is a bit long, including from the design stage, to the exploration stage, to the implementation stage, and finally comprehensive coverage of the testing stage (including data collection and cleaning), as well as the comparison with the mainstream front-end communication framework PK stage.

First, a few concepts:

  1. Browser concurrency: The browser design defines the number of moments when the browser can open a page and send an HTTP request. This is designed for a number of reasons, protecting both the browser and the server. Specific can be Google or Baidu keywords: browser concurrency.

2. The number of concurrent browser requests to the server domain name:

Description of concurrent browser requests

3. Request pool: Similar to database connection pool, database request connection allocation management and so on

4. Reuse request: do not destroy the request whose life cycle has ended, reuse it, reduce re-application to the browser for resources, reduce the common check in the design of the common library.


New features:The connection pool

Why we made this feature in the first place:

There was an Easter egg left at the end of the last iteration. In a test, AFTER sending a request, I saved the request in a variable and then tried to open it again, and found that it could be sent out. Curiosity killed the cat. I used console.time in my browser to calculate how long it took to execute this js code, and found that repeated requests took about half as long, or even less. Well, if he can be so good, then why can’t I just love it. So, this step down is a lot of twists and turns…

Groping for ideas:


Confirm step by step according to the design idea at that time:

1. After confirming that a request has been sent, you can open it again and send it. So that’s going to make sure that you’re going in the right direction, and you can keep optimizing

2. Secondly, for a domain name, the number of simultaneous requests sent by the browser is as described in the picture above, and the mainstream is basically 6. Therefore, the request pool will be configured based on the maximum number of concurrent connections of the browser, but the number of connections in the connection pool will be configured due to the differences of browsers

3. Filling the pool with configured requests would be the first task. For XMLHttpRequest, the prevailing idea is to make a copy of the object. But that plan died.

4. Because the prototype of the request Object is XMLHttpRequest, this Object implements the browser interface to send HTTP requests through the browser API, but the Object of the deep copy is Object, Object does not implement the browser interface, so it is aborted and cannot be copied directly. Do I want to collect a pool connection after sending six empty requests? This is absolutely impossible to forgive, so do the next step

5. If the XMLHttpRequest object implements the browser interface, we redirect the prototype of the deep-copy object to the XMLHttpRequest. Is it ok to send the request? Error: response, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText, responseText

6. Discouraged for a long time, empty your mind and find another way to solve the problem. Referring to other similar design ideas of the request pool, we either initialize the pool at the beginning and produce the connections in the pool at one time, or after each connection is consumed, we put the pool into the pool and wait until the connection is full.

7. Considering that the number of requests per page of the front-end is not very large, or for each component now, it is possible to send 4-5 requests under a route to solve a small business demand, which cannot reach the basic configuration of starting the request pool. Secondly, by monitoring page performance through browser performance, we found that the initialization time is only a few milliseconds, compared to the late use of acceleration, this time paid, for late use is well worth the budget. So, the first option is to initialize the request pool once and wait for the call. Below wisp clear thinking, made more complete and comprehensive design.

Request pool design scheme:


Left – Generates the request pool section

1. When loading ajax-JS library, check whether the request pool switch is turned on. If so, go through the initialization process.

2. Create an empty request based on the global configuration –> Copy required parameters –> Create an empty XMLHttpRequest object –> merge copy parameters onto an empty object –> read configuration quantity to generate a request pool

3. Since the request pool is generated based on the global configuration, if the global configuration changes, the reload of the request pool will be triggered and the request pool will be regenerated.

Middle – The life cycle of sending requests through the request pool

1. Request comes in, first determine whether the switch is turned on, then determine whether the number of requests in the request pool is still there.

2. Compare the parameters to see if any additional preset parameters are changed (except URL, data, successEvent, eroorEvent). If so, it will be set before the request is sent. (For example, the request Header will be completely reset each time it is opened)

3. Return the request pool at the end of the request lifecycle. Note: there are many situations at the end of the life cycle of the request, such as sending incorrect URL, timeout timeout and other failures at the browser level, and when the request is successfully sent, non-200, 304 errors, such as 4XX or 5XX series errors, end of the life cycle

4. Finally, judge whether there are queuing requests. If there are, take them out and return them to the pool

Right – request queuing system

1. If the number of requests exceeds the number of requests in the pool, the pool will not be able to handle them. Even if it does, the browser will queue up the requests and wait for the end of the wave before sending them. So it’s important to have a queuing system to manage requests.

2. Queue up requests that exceed capacity. Then, at the end of the request life, the queuing system is checked to see if there are any queued requests. Second, if there is a fetch, then go through the request life cycle, if per page, then there is no request


Code snippet display:

  1. Check the switch when loading the class library, initialize the code:
Var outputObj = function () {var outputObj = function () {var outputObj = function () { If (tool. GetIEVersion () < 7) {throw new Error("Sorry,please update your browser.(IE8+)"); } // Whether to enable the connection pool if (initparam.pool.isopen) {tool.createpool ()} return tempObj; };Copy the code

2. Create a method

CreatePool: function () {// IE series does not support sending requests "", so default/tempObj.com ({url: '/'}, true) tool.deepCloneXhr(selfData.xhr, initParam.pool.requestNumber) },Copy the code

3. Copy common parameters to the request pool

DeepCloneXhr: function (data, requestNum) {var mapping = {currentUrl: true, onError: true, onload: True, onreadystatechange: true, ontimeout: true, timeout: true, // the IE series only supports overrides withCredentials: true, xhr_ie8: true } var temp = {} for (var key in data) { if (mapping[key]) { if (! isNaN(tool.getIEVersion()) && key ! == 'timeout') { temp[key] = data[key] } else { var newKey = '_' + key temp[newKey] = data[key] } } } for (var i = 0; i < requestNum; i++) { var nullRequest = tool.createXhrObject() tool.MergeObject(nullRequest, temp) selfData.requestPool.push(nullRequest) } },Copy the code

4. Use acceleration to determine whether to enable (for example, POST)

Function (url, data, successEvent, errorEvent, timeoutEvent) {var ajaxParam = {type: "post", url: url, data: data, contentType: '', successEvent: successEvent, errorEvent: errorEvent, timeoutEvent: timeoutEvent }; if (initParam.pool.isOpen) { tool.useRequestPool(ajaxParam) } else { tempObj.common(ajaxParam); }},Copy the code

5. Set basic configuration (URL, data, successEvent) using request pool link

/ apply for/request pool request using useRequestPool: function (param) {/ / judge whether there is available in the request pool request if (selfData. RequestPool. Length! == 0) { var temp = selfData.requestPool.shift(), sendData = '', Temp. callback_success = param.successevent temp.callback_error = param.errorevent Temp. Callback_timeout = param.timeoutevent temp.data = param.data // Switch (param.contentType) {case ": tool.each(tool.MergeObject(param.data, initParam.publicData), function (item, index) { sendData += (index + "=" + item + "&") }); sendData = sendData.slice(0, -1); break case 'json': sendData = JSON.stringify(tool.MergeObject(param.data, initParam.publicData)) break case 'form': if (! tool.isEmptyObject(initParam.publicData)) { tool.each(initParam.publicData, function (item, index) { param.data.append(index, If (param.type === 'get') {temp.open(param.type, tool.checkRealUrl(param.url, temp) + (sendData === '' ? "' : ('? ' + sendData))) } else { temp.open(param.type, tool.checkRealUrl(param.url, temp)) } param.responseType ? (temp.responseType = param.responseType) : null if (! isNaN(tool.getIEVersion())) { temp.timeout = temp._timeout } switch (param.contentType) { case '': tempHeader['Content-Type'] = 'application/x-www-form-urlencoded' break case 'json': TempHeader [' content-type '] = 'application/json' break} // Set HTTP headers tool.each(tool.mergeObject (tempHeader, initParam.requestHeader), function (item, index) { temp.setRequestHeader(index, item) }); Temp. Send (param.type === 'get'? '' : sendData); Selfdata.queuepool.push (param)} else {// No request, load to queue selfdata.queuepool.push (param)}},Copy the code

6. Recycle links in the default cycle (onreadyStatechange and onLoad)

Xhr.onreadystatechange = function () {switch (this.readyState) {case 1:// open //do something break; Case 2:// Get the header //do something break; Case 3:// request //do something break; Case 4:// Done // Under IE8, no XHR onload event, Only in the processing and callback here if (this. Xhr_ie8) {if (this. The status = = = 200 | | this. The status = = = 304) {if (this. ResponseType = = "json") { this.callback_success ? this.callback_success(ajaxSetting.transformResponse(JSON.parse(this.responseText))) : ajaxSetting.successEvent(ajaxSetting.transformResponse(JSON.parse(this.responseText))) } else { this.callback_success ? this.callback_success(ajaxSetting.transformResponse(this.responseText)) : AjaxSetting. SuccessEvent (ajaxSetting. TransformResponse (enclosing the responseText))}} else {/ / request error collection tool. UploadAjaxError ({ type: 'request', errInfo: JSON.stringify(this.data ? this.data : ajaxSetting.data), errUrl: this.currentUrl, errLine: this.status, Browser: Navigator.useragent})} if (ajaxsetting.pool.isopen) {tool.responseover (this)}} else {if (this.status === 0) {// If the request does not exist, it will not go onload. If (ajaxsetting.pool.isopen) {tool.responseover (this)}} break; }; };Copy the code
/ / the onload event (not the event under the IE8) XHR. Onload = function (e) {if (this. ReadyState = = = 4 && (enclosing status = = 200 | | this. Status = = Parse (responseType='json') {responseType='json' and responseType=' responseType '; responseType='json' and responseType=' responseType '; isNaN(tool.getIEVersion())) { if (this.responseType === 'json') { this.callback_success ? this.callback_success(ajaxSetting.transformResponse(JSON.parse(this.responseText))) : ajaxSetting.successEvent(ajaxSetting.transformResponse(JSON.parse(this.responseText))); } else { this.callback_success ? this.callback_success(ajaxSetting.transformResponse(this.responseText)) : ajaxSetting.successEvent(ajaxSetting.transformResponse(this.responseText)); } } else { this.callback_success ? this.callback_success(ajaxSetting.transformResponse(this.response)) : ajaxSetting.successEvent(ajaxSetting.transformResponse(this.response)); }} else {/* * for compatibility with IE8, IE9, and other errors caused by request completion, For example, 404, etc. * If a cross-domain request fails in IE8 or IE9, do not go to onerror * */ this.callback_error? this.callback_error(e.currentTarget.status, e.currentTarget.statusText) : ajaxSetting.errorEvent(e.currentTarget.status, e.currentTarget.statusText); UploadAjaxError ({type: 'request', errInfo: json.stringify (this.data? this.data : ajaxSetting.data), errUrl: this.currentUrl, errLine: this.status, Browser: If (ajaxsetting.pool.isopen) {tool.responseover (this)}}; if (ajaxsetting.pool.isopen) {tool.Copy the code

7. Recycling method

// Call responseOver: function (xhr) { selfData.requestPool.push(xhr) if (selfData.queuePool.length > 0) { var tempData = selfData.queuePool.shift() tool.useRequestPool(tempData) } }Copy the code

The test results are shown below (test coverage: test itself against mainstream browsers without open request pool, open request pool, ajax comparison with mainstream frameworks AXIos and jquery, and send 10, 100, 1000 and 5000 consecutive requests in comparison accuracy)

Chrome (test unit: microseconds) :





Firefox (test unit: ms) PS: In the 5000 request test, except for enabling the request pool, all browsers crashed





Safari (measured in microseconds) :





Opera (Test unit: microseconds)





Edge (Test unit: ms)





IE11 (Test unit: ms)





IE9 (Test unit: ms)





IE8 (test unit: ms





Let’s start with a few questions:

1. Test the unit value

Use the console.time method of the browser to set the value. The parameters obtained in different browsers are different

2. Why is the value not agreed

Since Ajax-JS has achieved a level of 0.00x, or microseconds, in the time it takes to enable the request pool, which is not shown in the line chart, it increases units to see the trend

The following is summarized from the graphs obtained from all the cleaning data:

1. Jquery. ajax has the slowest speed and performance

2. Ajax-js provides the best and fastest performance when the request pool is enabled

3. The Ajax-JS class library does not turn on request pooling, which is relatively better than Ajax in jquery, but admittedly, AXIOS is better than Ajax-JS.

4. The Ajax-JS class library with request pooling enabled at least doubles performance on its own, much faster than AXIos


Data collected at: github.com/GerryIsWarr…

Report generated after data cleaning: github.com/GerryIsWarr…

Data cleaning tools: github.com/GerryIsWarr…

The data report formed after cleaning can be opened directly, so that you can see data more clearly

If you want to test, you can go to Github to download the project, in the Ajax-interface file, write an Express node server, can be initialized after NPM I, then NPM run start. There is then an HTML file in the Ajax-Testing directory that provides test cases and some simple demos.

All the methods exposed this time enable request pool acceleration (enable global acceleration configuration). Except the common method, one unaccelerated universal method is reserved to prevent special requirements. In addition, this request pool is only for one domain name acceleration, phase ii will add request pool for different domains acceleration.

Github address: github.com/GerryIsWarr… To help or inspire you, click on a little star and support your research


In this iteration, there were many twists and turns, and there were many times when it could not continue halfway. However, because the basic direction of the test was right at the beginning, I did not want to give up, and then I straightened out my thinking and went step by step to see where I was wrong and why I could not go in this direction. In addition, for this test, I did a lot of data collection, switching from MAC system to Window, and collecting data for compatibility test of mainstream browser and comparison test of mainstream framework. After collecting, I cleaned the data patiently, classified and summarized the mixed data, then found the corresponding report, and finally loaded the data into the report to make a more intuitive display.

The process is hard, and the results are exciting. Let’s face it, Axios is an excellent framework, and the speed and performance are definitely good. A self-written library would not have reached that level without request pooling, but with request pooling enabled, it already beats Axios in terms of performance and speed, not to mention jquery. In the study of front-end communication process harvest a lot of, from the foundation to design, from the bottom to optimization, there is no problem that can not be solved. Nothing is impossible to know who you are and be a better version of yourself.

Going from 0-1 is tough, going from 99-100 is even harder. However,, as long as to do, the end is closer to that direction, front end mutual encouragement!!