current position:Home>A front-end interview question -- implementing an encapsulated Ajax device (promise version)

A front-end interview question -- implementing an encapsulated Ajax device (promise version)

2022-04-29 09:18:12from_ the_ star

Title Description

Implement a package ajax device , Functions include

  1. Limit the number of messages sent at one time ajax Number of requests m individual
  2. timeout Limit
  3. retry n Time

Pre knowledge

XMLHttpRequest

XMLHttpRequest(XHR) Object is used to interact with the server . adopt XMLHttpRequest You can request specific... Without refreshing the page URL, get data . This allows web pages to , Update the local content of the page .XMLHttpRequest stay AJAX It's used a lot in programming

A brief introduction , You can go to MDN Check out .

open(): Initialize a request

send(): Request

abort(): If the request has been sent , Then stop the request immediately .

readyState、status attribute : Used to determine whether the request is completed . commonly readyState !== 4 && status === 0 It means that the request is completed .

timeout attribute : Indicates the maximum request time of the request ( millisecond ), Beyond that time , The request will automatically terminate .

Fetch

Fetch API Provides an interface to obtain resources ( Including cross domain requests ).fetch() Must accept a parameter —— The path of resources . Whether the request is successful or not , It all returns one Promise object ,resolve In response to the request Response. You can also pass an optional second parameter init( See Request).

Just mention what you need , We'd better go to MDN Check out .

fetch() Method is used to initiate a request to obtain resources . It returns a promise, This promise Will be... After the request response resolve, And send back Response object .

It should be noted that : When you receive a wrong HTTP When the status code , from fetch() Back to Promise Will not be marked as reject, Even if the response is HTTP Status code is 404 or 500, It will also Promise The status is marked with resolve ( But it will resolve The return value of Response Object's ok Property is set to false ). Only if the network fails or the request is blocked ,Promise The status will be marked as reject.

suspend fetch

The browser has started for AbortController and AbortSignal Interface ( That is to say Abort API) Add experimental support , Allow like Fetch and XHR Such an operation is aborted before it is completed .

Their thinking

ajax have access to XMLHttpRequest encapsulation , You can also use off the shelf Fetch Transform it to realize additional functions .

XMLHttpRequest

advantage : flexible , High operability , There is an abort request 、 timeout 、 A variety of event monitoring methods

shortcoming : trouble

Fetch

advantage : Easy to use , Send a request with credentials 、 Upload JSON 、 Common scenarios such as uploading file data can be covered

shortcoming : Only... Can be set Request object , No, XMLHttpRequest, Properties without timeout settings API

here xhr It's still used Fetch, I'm so lazy , I don't want to listen to events and judge , Encapsulating a series of get、post Method, etc. . about Fetch Properties without timeout settings API, With the help of timer and abort fetch Of API complete .

Implementation part

queue

I used a queue here , To save the sent ajax request , When the maximum number of requests is reached , Just keep it in the queue , Waiting for the connection .

Implement a queue , Generally, it is necessary to join the team 、 The way out of the team , In line with the principle of first in, first out , At the same time, we should also realize that the query team is full 、 The status of the team empty .
Because if you enter and leave the team too many times, the array will be too large , In order not to cause waste , What is implemented here is a circular queue .

// Implement a queue 
class Queue{
    
    constructor(size){
    
        this.size = size;
        this.data = Array(size);
        this.front = 0;
        this.rear = 0;
    }

    in(url){
    
        if(this.isFull()) return false;
        this.data[this.rear] = url;
        this.rear = (this.rear+1)%this.size;
        return true;
    }

    out(){
    
        let res = null;
        if(this.isEmpty()) return res;
        res =  this.data[this.front];
        this.data[this.front] = null;
        this.front = (this.front+1)%this.size;
        return res;
    }

    isFull(){
    
        return this.front===this.rear&&this.data[this.size-1];
    }

    isEmpty(){
    
        return this.front===this.rear&&!this.data[this.size-1];
    }

}

Main part

The code is implemented in get Request as an example , as follows :( The main ajax request 、 Retry and timeout settings are both in reTryRequest Function )

get requests( Request pool ) tryRequestUrl reTryRequest 1. Accept request parameters , Save to request pool , return Promise 2. Try to make a request 3. The maximum number of requests... Has not been exceeded , Request 4. retry 5. change Promise state 6. Notify the request pool to push the next request Request timed out or request failed , Number of retries not exceeded , Request again get requests( Request pool ) tryRequestUrl reTryRequest
class axios {
    
  /** *  initialization axios * @param maxSize  Limit simultaneous requests ajax Number of requests m * @param poolSize  Size of the request pool  */
  constructor(maxSize = 3, poolSize = 100) {
    
    this.requests = new Queue(poolSize);
    this.maxSize = maxSize;
    this.curSize = 0;
  }

  // only get, It can be set by value transfer fetch The second parameter to meet more needs 
  get(url,timeout,reTryCount) {
    
    let res,rej;
    const currentPro = new Promise((resolve, reject)=>{
    
      res = resolve;
      rej = reject;
    });
    if (!this.requests.isFull()) {
    
      this.requests.in([url,res,rej,timeout,reTryCount]);
      this.tryRequestUrl();
    } else {
    
      rej(new Error(" The request pool is full "));
    }
    return currentPro;
  }

  // Into the request pool , Start judging whether to send a request according to the maximum request 
  tryRequestUrl() {
    
    if (this.curSize < this.maxSize) {
    
      if(this.requests.isEmpty()){
    
        console.log(` Complete all requests `);
      }else{
    
        let [url,resolve,reject,timeout,reTryCount] = this.requests.out();
        if (url) {
    
          console.log(` Start connecting ${
      url}`);
          this.reTryRequest(url,resolve, reject,timeout,reTryCount);
          this.curSize++;
        }
      }
    } else {
    
      console.log(` Waiting for the connection `);
    }
  }

  //  Request and be responsible for retrying 
  reTryRequest(url,resolve, reject,timeout,reTryCount) {
    
    let TriedCount=0;
    if(!reTryCount) reTryCount = 0;
    
    const xhr = ()=>{
    
      let t;
      let controller = new AbortController();
      let signal = controller.signal;
      if(timeout){
    
         t = setTimeout(()=>{
    
          controller.abort();
        },timeout);
      }

      fetch(url,{
    signal})
      .then((response)=> {
    
        if(response.ok){
    
          t&&clearTimeout(t);
          resolve(response.json());
          this.curSize--;
          this.tryRequestUrl();
        }else{
    
          retryFunc()
        }
      })
      .catch((e)=>{
    
        retryFunc(e)
      })
    } 

    // Try to retry after failure 
    const retryFunc = (e)=>{
    
      if(TriedCount < reTryCount){
    
        TriedCount++;
        console.log(" Timeout start retry ");
        xhr();
      }else{
    
        reject(url+" The maximum number of retries has been reached ");
        this.curSize--;
        this.tryRequestUrl();
      }
    }
    xhr();
  }
}

Test code :

const a = new axios();
a.get("/simple/get",520,1).then((v)=>console.log(v));
a.get("/simple/get1",520,2).then((v)=>console.log(v));
a.get("/simple/get2",520,1).then((v)=>console.log(v));
a.get("/simple/get3",520,1).then((v)=>console.log(v));

Author's dish , If there is any wrong , Please point out , Thank you very much !

copyright notice
author[from_ the_ star],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2022/119/202204290630081780.html

Random recommended