What happens when page data comes from multiple requests? This is also a very common scenario, and we need to consider these points first:

  • Requests are not dependent on each other and can be processed concurrently
  • There are dependencies between requests and they need to be sequential
  • The Loading state is displayed before the request is complete

Speaking of requests, let’s talk a little bit about promises and async/await speakers!

Promise

The Promise object is used to represent the final completion (or failure) of an asynchronous operation and its resulting value.

A Promise must be in one of the following states:

  • Pending: The initial state that has neither been granted nor rejected.
  • This is a big pity: this will be fulfilled successfully.
  • Rejected: Indicates that the operation fails.

Chain calls: resolve corresponds to then; Reject corresponds to catch;

async/await

The async and await keywords allow us to write asynchronous behavior based on promises in a more concise way, without having to deliberately chain call promises.

Async keyword

async function hello() {
  return 'Hello';
}
// let hello = async function() { return "Hello" };
// let hello = async () => {
// return 'Hello';
// };
hello();
Copy the code

Calling this function returns a promise. This is one of the characteristics of asynchronous functions — it guarantees that the function returns a promise.

When a function is declared async, the JavaScript engine adds the necessary processing to optimize your program.

The await keyword

The real advantage of the await keyword becomes apparent when it is used with asynchronous functions – in fact, await only works within asynchronous functions. It can precede any asynchronous, promise-based function. It pauses the code on that line until the promise completes, then returns the result value. At the same time as the pause, other code waiting to be executed has a chance to execute.

defects

Async/await makes your code look synchronous and, in a way, makes it behave more synchronous. The await keyword blocks subsequent code until the promise completes, as if a synchronization operation had been performed. It does allow other tasks to continue running in the meantime, but your own code is blocked.

This means that your code may be slow due to lots of await promises happening one after another. Each await will wait for the previous one to finish, and what you really want is for all promises to start processing at the same time (just like when we are not using async/await).

One pattern alleviates this problem — start Promise objects simultaneously by storing them in variables, and then wait for them all to complete. Let’s look at some examples to prove this concept.

async function timeTest() {
  await timeoutPromise(3000);
  await timeoutPromise(3000);
  await timeoutPromise(3000);
}
Copy the code

The total running time is about 9 seconds.

async function timeTest() {
  const timeoutPromise1 = timeoutPromise(3000);
  const timeoutPromise2 = timeoutPromise(3000);
  const timeoutPromise3 = timeoutPromise(3000);

  await timeoutPromise1;
  await timeoutPromise2;
  await timeoutPromise3;
}
Copy the code

Here, we store the three Promise objects in variables that can start their associated processes at the same time. The total running time is just over 3 seconds!

The scenario

There are three requests:

  • Requesting user data
  • List of request types
  • Request Cities list

The logical relationship between them is:

  • First, initialize the user information, including name, province, and city
  • Update the list of cities when switching provinces

The implementation process

Simulation of the request

First, we use setTimeout to simulate the above three requests:

Fetch user information (fetchUserInfo)

const fetchUserInfo = async() = > {return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve({
        userName: 'Nate'.province: 'shanghai'.city: 'pudong'}); },2000);
  });
};
Copy the code

fetchProvices

const fetchProvices = () = > {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve([
        { name: 'Beijing'.key: 'beijing' },
        { name: 'Shanghai'.key: 'shanghai' },
        { name: 'jiangsu'.key: 'jiangsu' },
        { name: 'shandong'.key: 'shandong'},]); },2000);
  });
};
Copy the code

fetchCities

const fetchCities = (province) = > {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve(
        {
          beijing: [{name: 'sun'.key: 'chaoyang' },
            { name: 'haidian'.key: 'haidian'},].shanghai: [{name: 'in the pudong new area.key: 'pudong' },
            { name: 'xuhui'.key: 'xuhui'},].jiangsu: [{name: 'nanjing'.key: 'nanjing' },
            { name: 'in suzhou'.key: 'suzhou'},].shandong: [{name: 'Qingdao'.key: 'qingdao' },
            { name: 'Texas'.key: 'dezhou' },
          ],
        }[province],
      );
    }, 2000);
  });
};
Copy the code

UI design

Based on Ant Design, the main use of Form, Select, Input components. The list item is in loading state by default.

import { Form, Select, Input } from 'antd';

const { Option } = Select;

export default function MultipleRequestAsync(props) {
  return (
    <Form
      labelCol={{ span: 8 }}
      wrapperCol={{ span: 8 }}
      initialValues={{ province: 'loading...', city: 'loading...'}} >
      <Form.Item label="Name:" name="userName">
        <Input placeholder="user name" />
      </Form.Item>
      <Form.Item label="Province:" name="province">
        <Select style={{ width: 120}} >
          <Option value="loading">loading...</Option>
        </Select>
      </Form.Item>
      <Form.Item label="City:" name="city">
        <Select style={{ width: 120}} >
          <Option value="loading">loading...</Option>
        </Select>
      </Form.Item>
    </Form>
  );
}
Copy the code

The data processing

When initializing data, there are dependencies between three requests:

  • To get the data

    const [userInfo, setUserInfo] = useState({});
    const [provinces, setProvinces] = useState([]);
    const [cities, setCities] = useState([]);
    
    useEffect(() = > {
      async function getData() {
        const info = await fetchUserInfo();
        setUserInfo(info);
        const provinces = await fetchProvices();
        setProvinces(provinces);
        const cities = awaitfetchCities(info.province); setCities(cities); form.setFieldsValue(info); } getData(); } []);Copy the code
  • Render data

    • State list

      <Select style={{ width: 120 }} onChange={handleProvinceChange}>
        {provinces.length ? (
          provinces.map((provinces) = > (
            <Option key={provinces.key} value={provinces.key}>
              {provinces.name}
            </Option>))) : (<Option value="loading">loading...</Option>
        )}
      </Select>
      Copy the code
    • The city list

      <Select style={{ width: 120 }}>
        {cities.length ? (
          cities.map((city) = > (
            <Option key={city.key} value={city.key}>
              {city.name}
            </Option>))) : (<Option value="loading">loading...</Option>
        )}
      </Select>
      Copy the code
  • Update the list of cities when switching provinces

    const [form] = Form.useForm();
    
    const handleProvinceChange = async (newProvince) => {
      // Display intermediate loading state
      form.setFieldsValue({ city: 'loading' });
      // Pull a list of cities
      const cities = await fetchCities(newProvince);
      setCities(cities);
      // Set the default selection
      form.setFieldsValue({ city: cities[0].key });
    };
    Copy the code

The complete code

import { useState, useEffect } from 'react';
import { Form, Select, Input, Button } from 'antd';

const { Option } = Select;

const fetchUserInfo = async() = > {return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve({
        userName: 'Nate'.province: 'shanghai'.city: 'pudong'}); },2000);
  });
};

const fetchProvices = () = > {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve([
        { name: 'Beijing'.key: 'beijing' },
        { name: 'Shanghai'.key: 'shanghai' },
        { name: 'jiangsu'.key: 'jiangsu' },
        { name: 'shandong'.key: 'shandong'},]); },2000);
  });
};

const fetchCities = (province) = > {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve(
        {
          beijing: [{name: 'sun'.key: 'chaoyang' },
            { name: 'haidian'.key: 'haidian'},].shanghai: [{name: 'in the pudong new area.key: 'pudong' },
            { name: 'xuhui'.key: 'xuhui'},].jiangsu: [{name: 'nanjing'.key: 'nanjing' },
            { name: 'in suzhou'.key: 'suzhou'},].shandong: [{name: 'Qingdao'.key: 'qingdao' },
            { name: 'Texas'.key: 'dezhou' },
          ],
        }[province],
      );
    }, 2000);
  });
};

export default function MultipleRequestAsync(props) {
  const [userInfo, setUserInfo] = useState({});
  const [provinces, setProvinces] = useState([]);
  const [cities, setCities] = useState([]);
  const [form] = Form.useForm();

  useEffect(() = > {
    async function getData() {
      const info = await fetchUserInfo();
      setUserInfo(info);
      const provinces = await fetchProvices();
      setProvinces(provinces);
      const cities = awaitfetchCities(info.province); setCities(cities); form.setFieldsValue(info); } getData(); } []);const handleProvinceChange = async (newProvince) => {
    form.setFieldsValue({ city: 'loading' });
    const cities = await fetchCities(newProvince);
    setCities(cities);
    form.setFieldsValue({ city: cities[0].key });
  };

  return (
    <Form
      form={form}
      labelCol={{ span: 8 }}
      wrapperCol={{ span: 8 }}
      initialValues={{ province: 'loading...', city: 'loading...'}} >
      <Form.Item label="Name:" name="userName">
        <Input placeholder="user name" />
      </Form.Item>
      <Form.Item label="Province:" name="province">
        <Select style={{ width: 120 }} onChange={handleProvinceChange}>
          {provinces.length ? (
            provinces.map((provinces) => (
              <Option key={provinces.key} value={provinces.key}>
                {provinces.name}
              </Option>))) : (<Option value="loading">loading...</Option>
          )}
        </Select>
      </Form.Item>
      <Form.Item label="City:" name="city">
        <Select style={{ width: 120}} >
          {cities.length ? (
            cities.map((city) => (
              <Option key={city.key} value={city.key}>
                {city.name}
              </Option>))) : (<Option value="loading">loading...</Option>
          )}
        </Select>
      </Form.Item>
    </Form>
  );
}
Copy the code

React Best Practices

  • React Best practice: Create a drag-and-drop list by hand
  • React Best Practice: Integrating third-party Libraries (d3.js)
  • React Best Practices: How to Implement Native Portals
  • React best practice: Drag-and-drop sidebar
  • React best practice: Implement step-by-step operations based on routes
  • React best practice: Work with multiple data sources
  • React best practice: Complete a list of requirements
  • React best practice: Dynamic forms