当页面数据来自于多个请求时,咱们该怎么去处理呢?这也是一个很常见的场景,咱们须要先考虑这几点: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>
);
}
复制代码