DevUI is an open source front-end solution for enterprise middle and back-end products. It advocates immersion, flexibility, and to simplify the design values. It encourages designers to serve real needs, design for most people, and reject grandstanding. Eye-pleasing design. If you are developing ToB of tools products, DevUI would be a very good choice!

Kagol.png

introduction

Search function, I think many businesses will be involved, the characteristics of this function are:

  • The user can enter a keyword in the input box, and then display the data corresponding to the keyword in a list;
  • The input box can modify/delete all or part of the keywords at any time;
  • If it is a real-time search 🔍 (that is, the result will be displayed immediately after entering the keyword, no additional operation or excessive waiting is required), the interface call will be very frequent.

Real-time search will face a common problem, that is:

The browser requests backend interfaces are asynchronous. If the requested interface is initiated first and then the data is returned, the data displayed in the list/table is likely to be messy.

Reproduce the problem

A recent test mentions a search (PS: the search here 🔍 is implemented with the newly launched CategorySearch component of DevUI) related to the defect list, which involves the above problems.

1-1.png

The general meaning of this bug list is:

When searching, if you input or delete keywords in quick succession, the search results do not match the search keywords.

Judging from the screenshot of the defect list, the original intention is to search for the keyword 8.4.7 iteration], the actual search result in the table is 8.4.7 iteration] the keyword data.

The screenshot of the defect list also posted the two requested information very intimately:

2.png

As an "experienced" front-end developer, at first glance it is a general technical problem:

  1. Browser requests from the server are asynchronous;
  2. Since the previous request to the server was slow to return, the next request was initiated before the first request returned the result, and the result was returned quickly, then the table must show the result of the next time;
  3. After 2 seconds, the result of the first request was returned slowly. At this time, the form incorrectly displayed the result of the first request;
  4. Eventually led to this bug.

How to solve it?

Before thinking of a solution, you have to find a way to find this problem. It is unrealistic to rely on the background interface. In most cases, the background interface will quickly return results.

Therefore, if this problem is necessary, a slow interface must be simulated first.

Simulate slow interface

In order to quickly build a background service and simulate a slow interface, we choose Koa a lightweight Node framework.

Quick start

Koa is very convenient to use, you only need:

  1. New project folder: mkdir koa-server
  2. Create package.json: npm init -y
  3. Install Koa: npm i koa
  4. Write service code: vi app.js
  5. Start: node app.js
  6. Visit: http://localhost:3000/

Write service code

Use the following command to create the app.js startup file:

vi app.js

Enter the following 3 lines of code in the file to start a Koa service:

const Koa = require('koa'); // 引入 Koa
const app = new Koa(); // 创建 Koa 实例
app.listen(3000); // 监听 3000 端口

access

If the task service is not started on port 3000, visit in the browser:

http://localhost:3000/

The following page will be displayed:

3.png

After starting our Koa Server, visit:

http://localhost:3000/

Will show:

4.png

get request

What I just built is just an empty service, there is no route, so Not Found is displayed.

We can make our Koa Server show something through middleware.

Since we want to add a root route, we first install the route dependency

npm i koa-router

Then introduce Koa Router

const router = require('koa-router')();

Next is to write the get interface

app.get('/', async (ctx, next) => {
  ctx.response.body = '<p>Hello Koa Server!</p>';
});

Finally, don't forget to use routing middleware

app.use(router.routes());

After changing the code, you need to restart the Koa service. In order to facilitate restarting, we use pm2, a Node process management tool to start/restart the Koa service. It is also very simple to use:

  • Install pm2 globally: npm i -g pm2
  • Start Koa Server: pm2 start app.js
  • Restart Koa Server: pm2 restart app.js

After restarting Koa Server, visit again

http://localhost:3000/

The following will be displayed:

4-1.png

post request

With the above foundation, you can write a post interface to simulate a slow interface!

Writing the post interface is very similar to the get interface:

router.post('/getList', async (ctx, next) => {
  ctx.response.body = {
    status: 200,
    msg: '这是post接口返回的测试数据',
    data: [1, 2, 3]
  };
});

At this time, we can use Postman to call the post interface and return as expected:

5.png

Allow cross-domain

We try to call this post interface in the NG CLI project:

this.http.post('http://localhost:3000/getList', {
  id: 1,
}).subscribe(result => {
  console.log('result:', result);
});

But when you call it directly in the browser, you can't get the desired result:

  • result is not printed
  • Console error
  • Network requests are also red

6.png

Since the project port number (4200) of the locally launched project is different from the Koa Server (3000), the browser thinks this interface is cross-domain, so it is blocked.

Local link of NG CLI project:

http://localhost:4200/

Koa Server link:

http://localhost:3000/

Koa has a middleware that allows cross-domain: koa2-cors

The use of this middleware is very similar to routing middleware.

Install dependencies first:

npm i koa2-cors

Then introduce:

const cors = require('koa2-cors');

Use middleware again:

app.use(cors());

At this time, we will visit again:

http://localhost:4200/

You can get the result you want!

7.png

Slow interface

The post interface already exists, how to simulate a slow interface?

In fact, it is hoped that the server will delay returning the result.

Add the logic of delay before the post interface:

  async function delay(time) {
    return new Promise(function(resolve, reject) { 
      setTimeout(function() {
        resolve();
      }, time);
    });
  }

  await delay(5000); // 延迟 5s 返回结果

  ctx.response.body = { ... };

Visit the getList interface again and find that the previous interface will always be pending , and it takes only 5s to return the result.

8.png

9.png

Cancel slow interface request

If you can simulate a slow interface, you can easily test the questions!

The problem must be discovered first, and then try to fix the problem. Finally, we can see if the problem does not appear. If it does not appear, it means that our solution can solve the bug. The problem also means that we have to think of other ways.

This is the correct way to open the bug fix.

The most intuitive solution is to initiate the second request, if the first request does not return, then directly cancel this request and use the returned result of the second request.

How to cancel an http request?

Angular's asynchronous event mechanism is based on RxJS, and it is very convenient to cancel an ongoing http request.

We have seen earlier that Angular uses the HttpClient service to initiate http requests, and calls the subscribe method to subscribe to the returned results in the background:

this.http.post('http://localhost:3000/getList', {
  id: 1,
}).subscribe(result => {
  console.log('result:', result);
});

To cancel the http request, we need to save the subscription to a variable in the component:

private getListSubscription: Subscription;

this.getListSubscription = this.http.post('http://localhost:3000/getList', {
  id: 1,
}).subscribe(result => {
  console.log('result:', result);
});

Then, before re-initiating the http request, cancel the subscription for the last request.

this.getListSubscription?.unsubscribe(); // 重新发起 http 请求之前,取消上一次请求的订阅

this.getListSubscription = this.http.post(...);

How other http libraries cancel requests

So far this defect is solved. In fact, this is a general problem. No matter what business or framework you use, you will encounter data confusion problems caused by slow asynchronous interfaces.

So, if you use the native http request interface of the browser like fetch or axios , how to cancel the ongoing http request?

fetch

Let's first look at fetch. Fetch is an AJAX interface natively provided by the browser, and it is also very convenient to use.

Use fetch to initiate a post request:

fetch('http://localhost:3000/getList', {
   method: 'POST',
  headers: {
    'Content-Type': 'application/json;charset=utf-8'
  },
  body: JSON.stringify({
    id: 1
  })
}).then(result => {
  console.log('result', result);
});

You can use AbortController to achieve request cancellation:

this.controller?.abort(); // 重新发起 http 请求之前,取消上一次请求

const controller = new AbortController(); //  创建 AbortController 实例
const signal = controller.signal;
this.controller = controller;

fetch('http://localhost:3000/getList', {
   method: 'POST',
  headers: {
    'Content-Type': 'application/json;charset=utf-8'
  },
  body: JSON.stringify({
    id: 1
  }),
  signal, // 信号参数,用来控制 http 请求的执行
}).then(result => {
  console.log('result', result);
});

axios

Let's take a look at axios, let's take a look at how to use axios to initiate a post request.

Install first:

npm i axios

Introduce again:

import axios from 'axios';

Initiate a post request:

axios.post('http://localhost:3000/getList', {
  headers: {
    'Content-Type': 'application/json;charset=utf-8'
  },
  data: {
    id: 1,
  },
})
.then(result => {
  console.log('result:', result);
});

The request initiated by axios can be cancelled by cancelToken.

this.source?.cancel('The request is canceled!');

this.source = axios.CancelToken.source(); // 初始化 source 对象

axios.post('http://localhost:3000/getList', {
  headers: {
    'Content-Type': 'application/json;charset=utf-8'
  },
  data: {
    id: 1,
  },
}, { // 注意是第三个参数
  cancelToken: this.source.token, // 这里声明的 cancelToken 其实相当于是一个标记或者信号
})
.then(result => {
  console.log('result:', result);
});

summary

This article summarizes the common methods of defect analysis and resolution through the problems encountered in the actual project, and conducts an in-depth analysis of the data error problem caused by asynchronous interface requests.

join us

We are the DevUI team, welcome to come here to build an elegant and efficient man-machine design/development system with us. Recruitment email: muyang2@huawei.com.

Text/DevUI Kagol

Recommendations of previous articles

"Extra the nickname!" DevUI Admin V1.0 is released! 》

"Let's build the Vue DevUI project together! 🥳》

"CategorySearch category search component first experience"

"Interview with DevUI component library Wang Ge"

"The 7 most recommended Angular front-end component libraries in 2021"


DevUI团队
714 声望810 粉丝

DevUI,致力于打造业界领先的企业级UI组件库。[链接]