preface
I personally believe that the business side of the front end is pretty much of the work in dealing with the asynchronous request and organize all kinds of requests for assembly, the data such as user information from the log began to deal with the login show avator components, such as access to news list interface to get the data table show components such as business is very common. The more asynchronous requests you have in your business code, the less elegant the code that handles the request, the less expensive the maintenance and readability will be.
Say goodbye to try catch
Before I say goodbye to try catch, I want to talk a little bit about why we need try catch. If you don’t even know why you need a try catch, you’re going to get confused. Let’s start with the following code:
const callApi = () => { const a = 12; a.say(); Return axios.get('www.baidu.com/info', {id: 1}); } async function a() { await callApi() } a();Copy the code
The first common type of exception for asynchronous processing is the syntactic exception mentioned above. If there is no try catch in async/await function or the outermost code does not catch the exception, then the browser will report an error and the program will crash, which is a big event that every programmer does not want to happen.So we introduce try catch in async/await to catch such errors. Another class of exceptions is the request exception. Let’s start with the following code:
const callApi = () => {
return axios.get('www.baidu.com/info', {
id: 1
});
}
async function a() {
await callApi()
}
a();
Copy the code
Here I raise a request exception by visiting a 404 interface, and the browser crashes just as I like.
As a result, no matter what kind of exception it is, we start to avoid async/await out of awe of code crashing, and try to catch whatever await it is. As a result, the following code appears:
async componentDidMount() { try { const loginInfo = await axios.get('www.baidu.com/login', { id: 1 }); If (logininfo.success) {alert(' login succeeded '); } else { alert(loginInfo.description); } } catch(err) { console.log(err); } } getNewsList = async () => { try { const newsData = await axios.get('www.baidu.com/news', { id: 1 }); If (newsdata.success) {alert(' login successfully '); } else { alert(newsData.description); } } catch(err) { console.log(err); }}Copy the code
The reality is that, based on the unthinking try catch of one developer after another, the code will be flooded with try catches. All right, let’s get ready to say goodbye to try catch. Write the following code:
// Wrap a promise request, say catch, Function hRequest(p) {return p.hen (res => [res, undefined]). Catch (err => [undefined, err]); } // encapsulate axios request export async function Get(p: {url: string, params: any}) {const {url, params} = p; return await hRequest(axios.get(url, params)); } // async login(params) {const [res, err] = await Get({url: LOGINAPI, params}); If (err) {console.log(' login failed ')} else {// do something}}Copy the code
Very well, a lot of the try catch code in the business will be cut, and suddenly the code will be much halal. The above approach works well for request exception capture, but not for syntactic logic exceptions. So we need to try catch the code entry globally or register a global error listener. It’s also very simple. At this point, we can say goodbye to try catch code.
Embrace the ts
Why I want to embrace TS, because for the front end, it’s not just about developing the interaction and interface, dealing with the background, dealing with the interface, getting information are all daily development tasks. After using Ts, interface description and data type declaration can be carried out on the background interface. Compared with the previous interface logic without Ts, it will be very rigorous and greatly reduce the syntax error caused by data type. At the risk of being very abstract, let’s look at the code:This is a user information interface, and when we use this interface to constrain the return body of our login request, and we need user information anywhere, the editor will automatically interface with all the attributes and types, and when we try to use methods that are not of that data type, tsLint will report an error. It helped us solve a lot of potential bugs during development. Given the benefits of embracing Ts, let’s look at how we can structure our requests so that they can specify return types on request, making it easier for us to use them.
Function hRequest<T = any>(p: Promise<any>): [T, any] { return p.then(res => [res, undefined]).catch(err => [undefined, err]) as unknown as [T, any]; } export async function Get<T = any>(p: { url: string, params: any }): Promise<[T, any]> { const { url, params } = p; return await hRequest<T>(axios.get(url, params)); }Copy the code
How is the business code used?
Developers who have experience in TS development can easily recognize the main use of generics, the input parameters and output constraints, before the request of the generic constraints, the request result can be based on the interface for all kinds of convenient operations. If you need to learn more about TS-generics, you can check out my other article on TS-generics
More elegant constraints
In the connection between the front end and the background, the field structure returned by the background must have a certain design structure, such as:
interface UserInfo {
id: string
name: string
age: number
}
interface LoginRes {
success: boolean
data: UserInfo
description: string
code: number
}
interface NewsDetail {
id: string
content: string
time: number
}
interface NewsRes {
success: boolean
data: NewsDetail[]
description: string
code: number
}
Copy the code
You can see that there is a lot of duplication in the structure of these interfaces, so we can abstract out the parts of data and then request them in the business
interface FetchRes<T> {
success: boolean
data: T
description: string
code: number
}
Copy the code
However, this is not simple enough. We just want to pay more attention to the interface type of data itself during the request, so we wrap the FetchRes interface inside the request method as the default value of the generic.
function hRequest<T = any>(p: Promise<any>): [T, any] {
return p.then(res => [res, undefined]).catch(err => [undefined, err]) as unknown as [T, any];
}
export async function Get<T = any>(p: {url: string, params: any}): Promise<[FetchRes<T>, any]> {
writeHeader();
const {url, params} = p;
const [res, err] = await hRequest<FetchRes<T>>(axios.get(url, params));
if (err) {
return [res, err];
} else {
fetchLog(url, params, res);
return [getDataFromMock(res) as FetchRes<T>, err]
}
}
Copy the code
So our business code only needs to focus on the data itself.
conclusion
After the combination of the above, the asynchronous request code in the project is more simplified and halal, and the async/await code is no longer kept at a distance. The request state needs to be customized so that the deconstructed value can be determined. For the request subject after embracing TS, the corresponding data type and method can be used boldly after the agreement of the main interface.