Refuse to be an API-only documentation engineer. This article will give you the basics of Web development, especially XMLHttpRequest, from reinventing the wheel.

This is another TypeScript post, and it’s the end of the year, so allow me to settle in. The last project to reconfigure Vconsole in TypeScript had a source code parsing problem with Axios embedded in it. So, the topic of this post is how to refactor Axios from TypeScript and why I did it.

The purpose of repeating the wheel in TypeScript is not only to develop good development habits in TypeScript, but more importantly to understand the basics of library association. Only by focusing more on the basics can we get rid of the document engineer. (Ps: Use TypeScript to get rid of front-end documentation!)

This sharing includes the following:

  • Engineering introduction & development techniques
  • API implementation
  • XHR, XHR, XHR
  • HTTP,HTTP,HTTP
  • Unit testing

Project source code, sharing may miss some details of the implementation, need to see the source code, test cases basically run. Think about it, the 5W Star library, just implementing itself.

Project introduction

What is Axios?

Promise based HTTP client for the browser and node.js

Axios is an HTTP client based on Promise for browsers and NodeJS, which itself has the following features (√ indicates this feature for this project) :

  • √ Create the XMLHttpRequest => XHR implementation from the browser
  • √ Support the Promise API => XHR implementation
  • √ Request and response interception => Request interception
  • √ Convert request and response data => corresponding project directory/src/core/dispatchRequest.ts
  • √ Cancel Request Cancel the request
  • √ Automatically converting JSON data => Corresponding project directory/src/core/dispatchRequest.ts
  • √ The client supports preventing CSRF/XSRF => CSRF
  • Make an HTTP request from Node.js

This article will focus on the implementation of XHR on the browser side and will not cover HTTP under Node. If you’re willing to take a look at it layer by layer, you’ll find that implementing Axios is pretty easy. Explore!

Directory description

Let’s start with the table of contents.

The directory is pretty much the same as Axios, and core is the core code for the Axios class. Adapters are XHR core implementations and Cancel is the code associated with the cancellation request. Helpers are used for common tool functions. The karmap.conf.js and test directories are related to unit tests. .travis. Yml is used to configure online continuous integration, and build profiles can be configured in the README file on Github.

Parcel integration

The packaging tool uses Parcel to compile TypeScript with zero configuration. SRC directory in index.html. Simply add index.ts to the import file to complete hot update, TypeScript compilation, etc.

<body>
  <script src="index.ts"></script>
</body>
Copy the code

Parcel related:

# global install
yarn global add parcel-bundler

# start service
parcel ./src/index.html

# packaged
parcel build ./src/index.ts
Copy the code

Vscode debugging

After running the parcel command, a local server is started and the VScode debuggers can be configured from launch.json in the.vscode directory.

{
  "version": "0.2.0"."configurations": [{"type": "chrome"."request": "launch"."name": "Lanzar Chrome contra localhost"."url": "http://localhost:1234"."webRoot": "${workspaceRoot}"."sourceMaps": true."breakOnLoad": true."sourceMapPathOverrides": {
        ".. / *": "${webRoot}/*"}}}]Copy the code

After the configuration is complete, breakpoint debugging can be started by pressing F5.

TypeScript configuration

The overall TypeScript configuration and specification checking reference is as follows:

  • tsconfig.json

  • tslint

It is strongly recommended that you enable tsLint, install the vscode tsLint plugin and configure the following format in the.vscode directory:

{
  "editor.tabSize": 2."editor.rulers": [120]."files.trimTrailingWhitespace": true."files.insertFinalNewline": true."files.exclude": {
    "**/.git": true."**/.DS_Store": true
  },
  "eslint.enable": false."tslint.autoFixOnSave": true."typescript.format.enable": true."typescript.tsdk": "node_modules/typescript/lib"
}
Copy the code

If Prettier is installed, the two need to be aware of the style conflict, no matter what the plug-in is for formatting code, the only goal is to ensure the same formatting style of code. (It is best to follow the Lint specification).

Ps:.vscodeDirectories can be tracked with Git into version management, which makes the Clone repository more user-friendly.

Alternatively, you can quickly see where the current project problem is via the issues TAB in vscode’s control panel.

TypeScript snippet testing

There are times when you want to edit a piece of test code that you don’t want to write in a project (such as writing a deepCopy function in TypeScript). If you don’t want to leave the vscode editor, we recommend using quokka, a plug-in that executes scripts immediately.

  • If you need to import other libraries, refer to the Quokka configuration
  • To introduce a browser environment, install the jdom-Quokka-plugin globally in the Quokkajs project directory

And then it goes like this

({
  plugins: 'jsdom-quokka-plugin',
  jsdom: { html: `<div id="test">Hello</div>`}});const testDiv = document.getElementById('test');

console.log(testDiv.innerHTML);
Copy the code

An overview of the API

The first idea for refactoring is to look at the API provided by the documentation, or the index.d.ts declaration file. The best source code can be seen in its test case, usually will provide API-related tests, such as Axios API test case, the implementation of the API is shared as follows:

That’s a total of five apis, less than a gourd baby. Now you’re confident. Let’s go head to head.

Axios class

These apis can be collectively referred to as instance methods, and if there are instances, there must be classes. So before we get into the API implementation, let’s take a look at the Axios class.

Two attributes (defaults, interceptors) and a generic method (request, other methods like GET, POST, etc., are based on request with different parameters) really can’t be simpler.

export default class Axios {
  defaults: AxiosRequestConfig;
  interceptors: {
    request: InterceptorManager;
    response: InterceptorManager;
  };
  request(config: AxiosRequestConfig = {}) {
    // Request correlation
  }
  // Request extends to get, post, etc
}
Copy the code

Axios instance

By default, the Axios library exports Axios, an instance of Axios, rather than the Axios class itself. Instead of returning an instance of Axios directly, however, the context of the Axios instance method Request is set to Axios. So axios is of type function, not object. But since function is also Object, you can set properties and methods. So axios can either act like an instance or call axios(config) directly. The concrete implementation is as follows:

const createInstance = (defaultConfig: AxiosRequestConfig) = > {
  const context = new Axios(defaultConfig);
  const instance = Axios.prototype.request.bind(context);
  extend(instance, Axios.prototype, context);
  extend(instance, context);
  return instance;
};

axios.create = (instanceConfig: AxiosRequestConfig) = > {
  return createInstance(mergeConfig(axios.defaults, instanceConfig));
};

const axios: AxiosExport = createInstance(defaults);

axios.Axios = Axios;

export default axios;
Copy the code

Axios also provides an axios class property that can be inherited by other classes. A factory function is also exposed that accepts a configuration item parameter, making it easy for users to create multiple request instances with different configurations.

Axios is configured by default

Instead of looking at the source code, if we use a class, we should be most concerned with the constructor, what properties are set by default, and what properties can be modified. Axios is the default configuration of the request.

Let’s look at the default configuration:

const defaults: AxiosRequestConfig = {
  headers: headers(), / / request header
  adapter: getDefaultAdapter(), // XMLHttpRequest sends a specific implementation of the request
  transformRequest: transformRequest(), // Custom handles request-related data. By default, it provides a method to modify the content-type based on the requested data.
  transformResponse: transformResponse(), // Custom processing of response-related data. By default, a method is provided to convert respOne data to JSON format
  timeout: 0,
  xsrfCookieName: 'XSRF-TOKEN',
  xsrfHeaderName: 'X-XSRF-TOKEN',
  validateStatus(status: number) {
    return status >= 200 && status < 300; }};Copy the code

That said, if you use Axios, you should know what its default Settings are.

Axios passes in the configuration

Let’s look at the attributes of the request parameters accepted by Axios. The following parameter attributes are optional. TypeScript defines the types of these parameters in advance and verifies that they are of the correct type when the parameters are passed.

export interfaceAxiosRequestConfig { url? :string; // Request a linkmethod? :string; // Request methodbaseURL? :string; // The basic link of the requestxsrfCookieName? :string; / / CSRFxsrfHeaderName? :string; / / CSRFheaders? :any; // Request header Settingsparams? :any; // Request parametersdata? :any; / / request bodytimeout? :number; // Set timeoutwithCredentials? :boolean; / / CSRFresponseType? : XMLHttpRequestResponseType;// Response typeparamsSerializer? :(params: any) = > string; // Format the url query parameteronUploadProgress? :(progressEvent: any) = > void; // Upload handleronDownloadProgress? :(progressEvent: any) = > void; // Download the handlervalidateStatus? :(status: number) = > boolean; adapter? : AxiosAdapter; auth? :any; transformRequest? : AxiosTransformer | AxiosTransformer[]; transformResponse? : AxiosTransformer | AxiosTransformer[]; cancelToken? : CancelToken; }Copy the code

Request configuration

  • url
  • method
  • baseURL
export interfaceAxiosRequestConfig { url? :string; // Request a linkmethod? :string; // Request methodbaseURL? :string; // The basic link of the request
}
Copy the code

Let’s take a look at the relevant knowledge:

Url and method are parameters to the open method in XMLHttpRequest.

Open syntax: xhrReq. Open (method, URL, async, user, password);

Url is a DOMString that represents the URL from which the request was sent.

Note: null | undefined passed to accept DOMString method or parameters usually take its stringifies is “null” | “undefined”

Pass the following parameters in the native open method, and the actual request URL is as follows:

let xhr = new XMLHttpRequest();

// Assume that the current window.location.host is http://localhost:1234

xhr.open('get'.' '); // http://localhost:1234/
xhr.open('get'.'/'); // href http://localhost:1234/
xhr.open('get'.null); // http://localhost:1234/null
xhr.open('get'.undefined); // http://localhost:1234/undefined
Copy the code

You can see the default baseURL for Windows. The location. The host such as http://localhost:1234/undefined to the URL request is successful situation exists. When the front end dynamically transfers URL parameters, the parameters may be null or undefined. If the operation is not responded by the status code of Response, the result obtained at this time is different from the expected. This reminds me that JavaScript implicit conversion pits abound. (Here amway TypeScript and the ‘===’ operator)

In this case, using TypeScript can circumvent these issues during development. But if the assignment is dynamic (such as when the result of a request is returned as a URL parameter), you need to type the value and throw an error or cast it to another desired value if necessary.

BaseURL ({baseURL:’… ‘) {baseURL ({baseURL:’… ‘})

const isAbsoluteURL = (url: string) :boolean= > {
  // 1. Check whether the protocol is used, for example, http://
  return /^([a-z][a-z\d\+\-\.]*:)? \/\//i.test(url);
};
const combineURLs = (baseURL: string, relativeURL: string) :string= > {
  return relativeURL
    ? baseURL.replace(/ / / + $/.' ') + '/' + relativeURL.replace(/ ^ \ / + /.' ')
    : baseURL;
};
const suportBaseURL = (a)= > {
  // 2
  returnbaseURL && ! isAbsoluteURL(url) ? combineURLs(baseURL, url) : url; };Copy the code

Params and data

The difference between Params and Data when sending a request in AXIos is:

  • Params is added to the request string of the URL for GET requests.

  • Data is added to the request body for post requests.

params

Axios handles both assignment and serialization of params (you can customize the paramsSerializer function)

The buildURL file in the helpers directory generates the complete URL request.

data

XMLHttpRequest adds data to the request body using the send method.

The syntax is as follows:

send();
send(ArrayBuffer data);
send(ArrayBufferView data);
send(Blob data);
send(Document data);
send(DOMString? data);
send(FormData data);
Copy the code

You can see that there are several types of data:

  • ArrayBuffer
  • ArrayBufferView
  • Blob
  • Document
  • DOMString
  • FormData

If you want to know what types of data there are, you can read this article

Actual use:

var xhr = new XMLHttpRequest();
xhr.open('GET'.'/server'.true);

xhr.onload = function() {
  // After the request ends, write the processing code here
};

xhr.send(null);
// xhr.send('string');
// xhr.send(new Blob());
// xhr.send(new Int8Array());
// xhr.send({ form: 'data' });
// xhr.send(document);
Copy the code

In addition, the Content-Type header should be set using the setRequestHeader() method to specify the MIME Type of the data stream before the send () method is called, depending on the data Type.

Axios has a default (customizable) method in the transformRequest configuration item that modifies the request.

const transformRequest = (a)= > {
  return [
    (data: any, headers: any) = > {
      / /... Modify the headers based on the data type}]; };Copy the code

HTTP related

HTTP request methods

Axios provides a way to configure HTTP requests:

export interfaceAxiosRequestConfig { method? :string;
}
Copy the code

Optional configurations are as follows:

  • GET: Requests a representation of a specified resource. Requests using GET should only be used to GET data.
  • HEAD: The HEAD method requests a response that is identical to the response of the GET request, but without the response body.
  • POST: Used to submit an entity (data) to a specified resource, usually resulting in a change in status or side effects on the server.
  • PUT: Replaces all current representations of the target resource with the request payload.
  • DELETE: deletes the specified resource.
  • OPTIONS: Communication OPTIONS used to describe the target resource.
  • PATCH: Used to apply partial changes to resources.

Let’s take a look at HTTP requests

HTTP defines a set of request methods that indicate the operation to be performed on a given resource. Indicates the expected action to perform for a given resource. Although they can also be nouns, these request methods are sometimes referred to as HTTP verbs. Each request method implements different semantics, but some common characteristics are shared by a group: for example, a request method can be safe, idempotent, or cacheable.

  • Safe: To say that an HTTP method is secure means that it does not modify the server’s data. In other words, this is a read-only operation on the server. These methods are safe: GET, HEAD, and OPTIONS. Some insecure methods such as PUT and DELETE are not.

  • Idempotent: An HTTP method is idempotent, meaning that the same request being executed once has the same effect as being executed multiple times in a row, and the server state is the same. In other words, idempotent methods should not have side effects (except for statistical purposes). Methods like GET, HEAD, PUT, and DELETE are idempotent when implemented correctly, but POST is not. All safe methods are idempotent, too.

  • Cacheable: CACHEable, a response is a cacheable HTTP response that is stored for later retrieval and use to keep new requests on the server.

For space, look at MDN

The HTTP request header

Axios provides a way to configure HTTP headers:

export interfaceAxiosRequestConfig { headers? :any;
}
Copy the code

A request header consists of a name (case insensitive) followed by a colon (:) followed by a specific value (without a newline character). The boot whitespace before this value is ignored.

Request headers can be defined as HTTP headers that are used in HTTP requests and are independent of the request body. Certain headers such as Accept, Accept-*, If-* ‘allow conditional requests to be executed. Some request headers such as Cookie, user-Agent, and Referer describe the request itself to ensure that the server returns the correct response.

Not all HTTP headers that appear in a request are part of the request header, as is often the case in POST requestsContent-LengthIt’s actually an Entity Header that represents the size of the request body, although you could also call it a request header.

Header list

Axios sets different Content-Type and Accpect headers depending on the request method.

Set the request header

The.setrequestheader () method provided by the XMLHttpRequest object gives the developer a way to manipulate both headers and allows the developer to customize the header information in the request header.

XMLHttpRequest. SetRequestHeader () is to set the HTTP request header. This method must be called between the open() method and send() method. If you assign to the same header more than once, only one header with multiple values will be generated.

If the Accept attribute is not set, the send() value is the default value/for this attribute. **

For security, some request header values can only be set by the User Agent: Forbidden Header Names and Forbidden Response Header Names.

By default, when an AJAX request is sent, the following header information is attached:

The axios setup code is as follows:

// In the xhr.ts file in the Adapters directory:
if ('setRequestHeader' in requestHeaders) {
  // Set the request header through XHR's setRequestHeader method
  for (const key in requestHeaders) {
    if (requestHeaders.hasOwnProperty(key)) {
      const val = requestHeaders[key];
      if (
        typeof requestData === 'undefined' &&
        key.toLowerCase() === 'content-type'
      ) {
        delete requestHeaders[key];
      } else{ request.setRequestHeader(key, val); }}}}Copy the code

As for whether you can change the HTTP header, my advice is that of course you can’t just change any field.

  • Some fields must not be modified, such as the all-important host field. If there is no host value, the HTTP1.1 protocol will consider it a noncanonical request and discard it. Similarly, if you change this value arbitrarily, the destination site will not return the correct content

  • User-agent is not recommended to be modified casually. There are many websites that adapt content according to this field. For example, PC and mobile phone must have different content.

  • Some fields can be modified, such as Connection, cache-control, etc. It will not affect your normal access, but it may be a bit slower.

  • There are also some fields that can be deleted. For example, if you don’t want the site to record your visit behavior or historical information, you can delete fields like cookie and referfer.

  • Of course, you can customize any field you want, and it usually doesn’t matter, unless the header is too long to truncate the content. Usually custom fields start with an X-. Like X-test: Lance.

HTTP summary

These header fields are automatically generated by the browser whenever the user actively enters an HTTP request, such as host, cookie, user-agent, accept-encoding, etc. JS can control the browser request, and it can also add some headers. However, for security and performance reasons, there are some limitations on the JS control of headers, such as host and cookie, user-agent, etc. JS is an untampered message header that forbids modification. There is a lot of knowledge out there about HTTP, and I’ll briefly cover the related knowledge here. Here are some hints for future examples that are more appropriate for HTTP.

The next CSRF will modify the headers.

CSRF

There are three configuration properties associated with CSRF:

export interfaceAxiosRequestConfig { xsrfCookieName? :stringxsrfHeaderName? :stringwithCredentials? :boolean;
}

// The default value is
{
  xsrfCookieName: 'XSRF-TOKEN',
  xsrfHeaderName: 'X-XSRF-TOKEN',
  withCredentials: false
}
Copy the code

So, let’s take a quick look at CSRF

Cross-site request forgery Cross-site Request Forgery, also known as one-click attack or session riding, usually abbreviated as CSRF or XSRF, Is a method of hijacking a user to perform unintended actions on a currently logged Web application. In contrast to cross-site scripting (XSS), which exploits the user’s trust in a given site, CSRF exploits the site’s trust in the user’s Web browser.

What is a CSRF attack?

You can think of a CSRF attack this way: An attacker steals your identity and sends malicious requests on your behalf. CSRF can do a lot of things: send emails on your behalf, send messages, steal your account, even buy goods and transfer virtual currency. The problems include personal privacy leakage and property security.

CSRF principle

On their phishing site, an attacker can create a request for your site by creating an AJAX button or form:

<form action="https://my.site.com/me/something-destructive" method="POST">
  <button type="submit">Click here for free money!</button>
</form>
Copy the code

To complete a CSRF attack, the victim must complete two steps in sequence:

1. Log in to trusted website A and generate cookies locally.

2. Visit dangerous website B without logging out of A.

How to mitigate CSRF attacks?

Use only the JSON API

Making AJAX requests using JavaScript is cross-domain restricted. You can’t send JSON through a simple

, so by only receiving JSON, you can reduce the likelihood of that happening.

Disable CORS

The first way to mitigate CSRF attacks is to disable cross-Origin requests. If you want to allow cross-domain requests, then only allow OPTIONS, HEAD, and GET methods, as they have no side effects. Unfortunately, this will not block the above request as it does not use JavaScript(so CORS does not apply).

Check the Referer field

The HTTP header has a Referer field that identifies the address from which the request came. When handling sensitive data requests, the Referer field should generally be in the same domain as the requested address. This method is simple, low workload, only need to add a check at the critical access point. This approach has its limitations, however, as it relies entirely on the browser to send the correct Referer field. Although the HTTP protocol specifies the content of this field, there is no guarantee that the specific implementation of the visiting browser or that the browser does not have security vulnerabilities that affect this field. It is also possible for an attacker to tamper with the Referer field of some browsers. (PS: See how important it is to follow Web standards)

CSRF Tokens

The ultimate solution is to use CSRF Tokens. How does CSRF Tokens work?

  1. The server sends a token to the client.
  2. The client submits the form with this token.
  3. If the token is not valid, the server rejects the request.

An attacker needs to get your site’s CSRF token somehow, and they can only do that using JavaScript. So, if your site does not support CORS, there is no way for them to obtain CSRF tokens, reducing the threat.

Make sure the CSRF token is not accessible through AJAX!

Do not create a /CSRF route to get a token, especially do not support CORS on this route!

Token needs are not easy to guess, making it difficult for an attacker to try a few times to get. It doesn’t need to be password secure. The attack comes from one or two clicks from an unknown user, not a violent attack from a server.

CSRF Tokens in AXIOS

So there’s the withCredentials, so let’s look at them first.

XMLHttpRequest withCredentials attribute is a Boolean type, It indicates whether a qualification such as cookies, Authorization Headers, or TLS client certificates should be used to create a cross-site Access-control request. Using the withCredentials attribute at the same site is invalid.

If withCredentials are not set to true before sending XMLHttpRequest requests from other domains, then it cannot set cookie values for its own domain. Third-party cookies obtained by setting withCredentials to true will still enjoy the same origin policy and therefore cannot be accessed through document.cookie or scripts requested from the header.

// In standard browser environments (non-Web worker or React-native) add the XSRF header
if (isStandardBrowserEnv()) {
  The xsrfHeader header must be set only in the case of withCredentials or cognates
  const xsrfValue =
    (withCredentials || isURLSameOrigin(url)) && xsrfCookieName
      ? cookies.read(xsrfCookieName)
      : undefined;
  if(xsrfValue && xsrfHeaderName) { requestHeaders[xsrfHeaderName] = xsrfValue; }}Copy the code

CSRF summary

For CSRF, sensitive requests such as GET should not be idempotent. However, since POST requests initiated by the Form Form are not restricted by CORS, Cookie of other fields can be arbitrarily used to send POST requests to other fields. CSRF attack is formed.

In this case, if there is a request involving sensitive information, you need to cooperate with the backend students to implement XSRF-Token authentication. At this point, we use axios request, can by setting the XMLHttpRequest. WithCredentials = true and set axios ({xsrfCookieName: ‘ ‘, xsrfHeaderName: ‘}). If not, the default xSRF-token and X-XSRF-token will be used.

So, in the AXIos feature, the client supports CSRF/XSRF prevention. It is only convenient to set up corF-token, but the key is to support the interface of the backend students. (PS: How important it is for the front and back end to love each other, so we, as the front end, should learn as much as we can about this)

XHR implementation

Axios provides two implementations of NODE.js HTTP and client-side XMLHttpRequest via the adapter pattern. This article focuses on the XHR implementation.

The approximate implementation logic is as follows:

const xhrAdapter = (config: AxiosRequestConfig): AxiosPromise= > {
  return new Promise((resolve, reject) = > {
    let request: XMLHttpRequest | null = new XMLHttpRequest();
    setHeaders();
    openXHR();
    setXHR();
    sendXHR();
  });
};
Copy the code

If you can explain the hardware line by line, make a tutorial video and go to the Xhr. ts in the Adapters directory with notes in key areas.

  1. The xhrAdapter accepts the config parameters (the combined values of the default parameters and the parameters passed in when the user instantiates them, and axios does special processing on the combined values).
  2. Set the request header, for example, based on the parameters passed indata.auth.xsrfHeaderNameSet the corresponding headers
  3. setXHRMainly in therequest.readyState === 4Response data processing and error handling
  4. The last executionXMLHttpRequest.sendmethods

It returns a Promise object, so all Promise features are supported.

Request to intercept

Request interception is one of the more elegant operations in Axios and is very simple to implement. Sort of like a series of promises that are executed sequentially.

Look directly at the code implementation:

  // Interceptors are divided into request and response.

  interface interceptors {
    request: InterceptorManager;
    response: InterceptorManager;
  }

  request (config: AxiosRequestConfig = {}) {
    const { method } = config
    constnewConfig: AxiosRequestConfig = { ... this.defaults, ... config, method: method ? method.toLowerCase() :'get'
    }

    // Interceptor principle: request interceptor, send request, respond to interceptor order execution

    Select * from [resolve, reject];
    // If there is no interceptor, the request will be sent.
    // Undefined by default as resolve and reject are used. What a SAO operation!

    const chain = [ dispatchRequest, undefined ]

    // 2. The Promise passes the parameters down when it succeeds, so the merged parameters are passed first for future interceptors to use (if any).
    let promise: any = Promise.resolve(newConfig)

    // 3, another wave of operations, perfect use of the array method. How to implement promise sequential execution without reduce?
    // This is a big surprise when [interceptor. This is a big pity, interceptor. Rejected, dispatchRequest, undefined]
    this.interceptors.request.forEach((interceptor: Interceptor) = > {
      chain.unshift(interceptor.fulfilled, interceptor.rejected)
    })
    / / response response interceptor is sure need to ` dispatchRequest ` behind, so [dispatchRequest, undefined, interceptor fulfilled, interceptor. The rejected]
    this.interceptors.response.forEach((interceptor: Interceptor) = > {
      chain.push(interceptor.fulfilled, interceptor.rejected)
    })

    // this is a big pity, my Promise will be fulfilled.
    while (chain.length) {
      promise = promise.then(chain.shift(), chain.shift())
    }

    return promise
  }
Copy the code

Again, it’s a good use of the basics, both Promise and array variation.

Of course, promises could be executed sequentially as follows:

function sequenceTasks(tasks) {
  function recordValue(results, value) {
    results.push(value);
    return results;
  }
  var pushValue = recordValue.bind(null[]);return tasks.reduce(function(promise, task) {
    return promise.then(task).then(pushValue);
  }, Promise.resolve());
}
Copy the code

Cancel the request

If you don’t know that XMLHttpRequest has the absort method, you might think it’s impossible to cancel the request. (PS: How important is basic knowledge)

const { cancelToken } = config;
const request = new XMLHttpRequest();

if (cancelToken) {
  cancelToken.promise
    .then(cancel= > {
      if(! request) {return;
      }
      request.abort();
      reject(cancel);
      request = null;
    })
    .catch(err= > {
      console.error(err);
    });
}
Copy the code

CancelToken is a strange implementation. Did not comprehend the original author’s design true meaning!

Unit testing

Finally, unit testing, we’ll take a look at dependencies.

Using Karma, the configuration is as follows:

Execute command:

yarn test
Copy the code

This project is based on Jasmine to write test cases, or relatively simple.

Karma runs all the test cases in the test directory and feels a little uncomfortable when the test cases are written in TypeScript. Because testing is supposed to diversify arguments, TypeScript dictates data types in advance. Although you can use generics to solve this problem, it always feels a little twisted.

However, as the test case ran through, the code became much stronger. It is necessary for this kind of library. If you need a secondary refactoring, it should be much easier with TypeScript and unit testing support that covers most functions.

conclusion

Thanks to those of you who are fans of TypeScript or Axios, get to know each other.

Again, TypeScript works. You can refactor Axios in a short amount of time, if you’re interested. As usual, there will be no details on how to use the library in the share. , more from the breadth of our knowledge. If a key word is unfamiliar, this is the time to make progress. For example, I’m going to dive into HTTP. However, it doesn’t feel like TypeScript is very popular right now. Good things never change. Ha, don’t hit me in the face.

Am I stronger? No, listen to Yang Zongwei’s “I have changed, I have not changed”.

Remember, there is no bug that can’t be solved by looking at the source code.

reference

  • Cross-site request forgery

  • Cross site scripting

  • The HTTP request

  • HTTP cache headers – Complete guide

  • XMLHttpRequest