當頁面數據來自於多個請求時,咱們該怎麼去處理呢?這也是一個很常見的場景,咱們須要先考慮這幾點:react
說到請求,不如先來簡單講講 Promise 和 async/await 叭!promise
Promise 對象用於表示一個異步操做的最終完成(或失敗)及其結果值。markdown
一個 Promise 必然處於如下幾種狀態之一:antd
鏈式調用:resolve 函數對應 then;reject 函數對應 catch;併發
async 和 await 關鍵字讓咱們能夠用一種更簡潔的方式寫出基於 Promise 的異步行爲,而無需刻意地鏈式調用 promise。app
async function hello() {
return 'Hello';
}
// let hello = async function() { return "Hello" };
// let hello = async () => {
// return 'Hello';
// };
hello();
複製代碼
調用該函數會返回一個 promise。這是異步函數的特徵之一 —— 它保證函數的返回值爲 promise。異步
在函數聲明爲 async 時,JavaScript 引擎會添加必要的處理,以優化你的程序。async
當 await 關鍵字與異步函數一塊兒使用時,它的真正優點就變得明顯了 —— 事實上, await 只在異步函數裏面才起做用。 它能夠放在任何異步的,基於 promise 的函數以前。它會暫停代碼在該行上,直到 promise 完成,而後返回結果值。在暫停的同時,其餘正在等待執行的代碼就有機會執行了。函數
Async/await 讓你的代碼看起來是同步的,在某種程度上,也使得它的行爲更加地同步。 await 關鍵字會阻塞其後的代碼,直到 promise 完成,就像執行同步操做同樣。它確實能夠容許其餘任務在此期間繼續運行,但您本身的代碼被阻塞。post
這意味着您的代碼可能會由於大量 await 的 promises 相繼發生而變慢。每一個 await 都會等待前一個完成,而你實際想要的是全部的這些 promises 同時開始處理(就像咱們沒有使用 async/await 時那樣)。
有一種模式能夠緩解這個問題——經過將 Promise 對象存儲在變量中來同時開始它們,而後等待它們所有執行完畢。讓咱們看一些證實這個概念的例子。
async function timeTest() {
await timeoutPromise(3000);
await timeoutPromise(3000);
await timeoutPromise(3000);
}
複製代碼
總運行時間大約爲 9 秒。
async function timeTest() {
const timeoutPromise1 = timeoutPromise(3000);
const timeoutPromise2 = timeoutPromise(3000);
const timeoutPromise3 = timeoutPromise(3000);
await timeoutPromise1;
await timeoutPromise2;
await timeoutPromise3;
}
複製代碼
在這裏,咱們將三個 Promise 對象存儲在變量中,這樣能夠同時啓動它們關聯的進程。總運行時間僅超過 3 秒!
有如下三個請求:
他們之間的邏輯關係是:
首先,咱們用 setTimeout 模擬上述三個請求:
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: '北京', key: 'beijing' },
{ name: '上海', key: 'shanghai' },
{ name: '江蘇', key: 'jiangsu' },
{ name: '山東', key: 'shandong' },
]);
}, 2000);
});
};
複製代碼
const fetchCities = (province) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(
{
beijing: [
{ name: '朝陽', key: 'chaoyang' },
{ name: '海淀', key: 'haidian' },
],
shanghai: [
{ name: '浦東', key: 'pudong' },
{ name: '徐匯', key: 'xuhui' },
],
jiangsu: [
{ name: '南京', key: 'nanjing' },
{ name: '蘇州', key: 'suzhou' },
],
shandong: [
{ name: '青島', key: 'qingdao' },
{ name: '德州', key: 'dezhou' },
],
}[province],
);
}, 2000);
});
};
複製代碼
基於 Ant Design,主要用到了 Form, Select, Input
組件。 列表項默認爲 loading 狀態。
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="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>
);
}
複製代碼
初始化數據時,三個請求之間存在依賴關係:
獲取數據
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 = await fetchCities(info.province);
setCities(cities);
form.setFieldsValue(info);
}
getData();
}, []);
複製代碼
渲染數據
省份列表
<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>
複製代碼
城市列表
<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>
複製代碼
切換省份時,更新對應的城市列表
const [form] = Form.useForm();
const handleProvinceChange = async (newProvince) => {
// 顯示中間loading狀態
form.setFieldsValue({ city: 'loading' });
// 拉取城市列表
const cities = await fetchCities(newProvince);
setCities(cities);
// 設置默認選擇值
form.setFieldsValue({ city: cities[0].key });
};
複製代碼
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: '北京', key: 'beijing' },
{ name: '上海', key: 'shanghai' },
{ name: '江蘇', key: 'jiangsu' },
{ name: '山東', key: 'shandong' },
]);
}, 2000);
});
};
const fetchCities = (province) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(
{
beijing: [
{ name: '朝陽', key: 'chaoyang' },
{ name: '海淀', key: 'haidian' },
],
shanghai: [
{ name: '浦東', key: 'pudong' },
{ name: '徐匯', key: 'xuhui' },
],
jiangsu: [
{ name: '南京', key: 'nanjing' },
{ name: '蘇州', key: 'suzhou' },
],
shandong: [
{ name: '青島', key: 'qingdao' },
{ name: '德州', 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 = await fetchCities(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="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>
);
}
複製代碼