最近写代码,发现涉及到promise的地方,有时候写了return,有时候没写return,promise了解的还不够清楚,现整理一下。
promise.then()
const fnc = () => {
return new Promise((resolve) => {
resolve("返回值");
});
};
const cb = () => {
return "新的值";
};
fnc()
.then(() => {
return cb();
})
.then((res) => console.log(res)); // 新的值
fnc()
.then(() => {
cb();
})
.then((res) => console.log(res)); // undefined
fnc()
.then(cb())
.then((res) => console.log(res)); // 返回值
promise.then(onFulfilled, onRejected)接收两个参数,一个是状态变为resolve后的回调函数,一个是状态变为reject后的回调函数(此处只讨论onFulfilled)
- 如果onFulfilled是一个函数,且有返回值,则返回值可以继续传递给后续的then
- 如果onFulfilled是一个函数,但没有返回值(相当于return undefined),则后续then中的入参为undefined
- 如果onFulfilled不是函数,则忽略当前then,将参数直接传递给后续的then
如果then中又有其他的promise呢?
fnc()
.then((res) => {
new Promise((resolve) => {
setTimeout(resolve, 5000, 123);
});
})
.then((res) => {
console.log(res);
}); // undefined, 5s后定时器结束,即then没有等待前面的Promise
fnc()
.then((res) => {
// 这里有return
return new Promise((resolve) => {
setTimeout(resolve, 5000, 123);
});
})
.then((res) => {
console.log(res);
}); // 5s后输出123
注意两种方法的区别,如果没有return,这里的promise会成为一个副作用,然后把undefined传递给后续的then, then并不会等待这个promise
只有当onFulfilled的返回值是一个Promise时,后续then才会等该promise状态变化后才执行
所以写then时保持有return或者throw的好习惯
- return 另一个promise
- return 一个同步的值或者undefined
- throw一个异常
书写风格
请求接口a,a接口返回结果后请求接口b,b接口返回结果后请求接口c
const interfaceA = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve("接口a返回的数据");
}, 1000);
});
};
const interfaceB = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve("接口b返回的数据");
}, 1000);
});
};
const interfaceC = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve("接口c返回的数据");
}, 1000);
});
};
// 不好的书写风格
interfaceA().then((res) => {
console.log(res);
interfaceB().then((res) => {
console.log(res);
interfaceC().then((res) => {
console.log(res);
});
});
});
// 使用return正确的书写风格
interfaceA()
.then((res) => {
console.log(res);
return interfaceB();
})
.then((res) => {
console.log(res);
return interfaceC();
})
.then((res) => {
console.log(res);
});
注意事项
- promise一定要有resolve或者reject
一个场景
- 转换接口返回格式为需要的格式
我们基于antd封装了一个带分页的请求后端接口的MyTable组件
// 使用时 index.js
import React from 'react'
import { MyTable } from './table'
// 模拟后端接口,总共有50条数据,每次返回当前页中的10条
const getPageData = page => {
return new Promise((reslove) => {
let source = []
const cur = (page - 1) * 10
for (let i = cur; i < cur + 10; i++) {
source.push({
key: '1',
name: '小瓦',
age: i,
address: '西湖区湖底公园1号',
})
}
setTimeout(() => {
const res = { total: 50, data: source }
reslove(res)
}, 1000)
})
}
export const Demo = () => {
const getList = (page) => {
return getPageData(page) // 一定要加return
}
return <MyTable query={getList} />
}
// table.js
import React, { useEffect, useState } from 'react'
import { Table } from 'antd'
const columns = [
{
title: '姓名',
dataIndex: 'name',
key: 'name',
},
{
title: '年龄',
dataIndex: 'age',
key: 'age',
},
{
title: '住址',
dataIndex: 'address',
key: 'address',
},
]
export const MyTable = (props) => {
const { query } = props
const [total, setTotal] = useState(0)
const [data, setData] = useState([])
const [page, setPage] = useState(1) // 当前页码
useEffect(() => {
query(page)
.then((res) => {
const { total, data } = res // 这里得确保,query返回的res是{total, data}格式
setData(data)
setTotal(total)
})
.catch(() => {})
}, [page, query]) // page改变时请求接口
return (
<Table
dataSource={data}
columns={columns}
pagination={{
total: total,
defaultCurrent: 1,
current: page,
onChange: (page, pageSize) => setPage(page),
}}
/>
)
}
问题来了,如果有个后端接口放回的数据格式并不是{total, data}这样的,比如接口返回了{all, list},那么这个组件就不能复用了,当然可以直接跟后端沟通,要求返回字段格式,不过我们也可以转一下接口格式。
// 之前
export const Demo = () => {
const getList = (page) => {
return getPageData(page) // getPageDate返回{all, list}
}
return <MyTable query={getList} />
}
// 转换
export const Demo = () => {
const getList = (page) => {
return getPageData(page).then(res=>{
return {
// 一定要加return
total: res.all
data: res.list
}
})
}
return <MyTable query={getList} />
}
async await
async和await可以更加语义化的解决回调地域问题,前面的“请求接口a,a接口返回结果后请求接口b,b接口返回结果后请求接口c”的场景,可以用 async + await 改写。
// 使用promise
interfaceA()
.then((res) => {
console.log(res);
return interfaceB();
})
.then((res) => {
console.log(res);
return interfaceC();
})
.then((res) => {
console.log(res);
});
// 使用async + await,更加语义化
const fn = async () => {
const res1 = await interfaceA()
const res2 = await interfaceB()
const res3 = await interfaceC()
}
await只是一种语法糖,只有当后面为promise时才会等待,原理同promise.then
// then中没有return
const fn = () => {
return new Promise((reslove) =>
setTimeout(() => {
console.log("第一个promise");
reslove("返回值");
}, 1000)
).then((res) => {
new Promise((reslove) => {
setTimeout(() => {
console.log("第二个promise");
reslove(res);
}, 1000);
});
});
};
const call = async () => {
const res = await fn();
console.log("async函数");
console.log("resres", res);
};
call(); // 1s后输出'第一个promise\async函数\resres,undefined',再1s后输出'第二个promise'
// then中有return
const fn = () => {
return new Promise((reslove) =>
setTimeout(() => {
console.log("第一个promise");
reslove("返回值");
}, 1000)
).then((res) => {
return new Promise((reslove) => {
setTimeout(() => {
console.log("第二个promise");
reslove(res);
}, 1000);
});
});
};
const call = async () => {
const res = await fn();
console.log("async函数");
console.log("resres", res);
};
call(); // 1s后输出'第一个promise',再1s后输出'第二个promise\async函数\resres,返回值'