前言:最近新开了个RN的项目,网络请求用的是fetch,和以前的项目完全不一样,以前都是用的 redux + redux-promise + redux-saga 这一套来处理异步请求的。而在新项目中我们这边使用ES6的 async/await 来处理异步问题。
今天介绍一下我学习 async/await 的过程:
首先,说一下async的用法,它作为一个关键字放到函数前面,用于表示函数是一个异步函数(async本身就是异步的意思), 异步函数也就意味着该函数的执行不会阻塞后面代码的执行。
其次,await必须在有关键字async的函数中使用
最后,async 函数返回的是一个promise 对象,如果要获取到promise 返回值,我们应该用then 方法,代码如下:
async function timeout() {
return 'hello world'
}
timeout().then(result => {
console.log(result);
})
console.log('虽然在后面,但是我先执行');
上面代码得到的效果:
注意:Promise 有一个resolved,这是async 函数内部的实现原理。如果async 函数中有返回一个值 ,当调用该函数时,内部会调用Promise.solve() 方法把它转化成一个promise 对象作为返回,但如果timeout 函数内部抛出错误呢? 那么就会调用Promise.reject() 返回一个promise 对象。
示例代码:
async function timeout(flag) {
if (flag) {
return 'hello world'
} else {
throw 'my god, failure'
}
}
console.log(timeout(true)) // 调用Promise.resolve() 返回promise 对象。
console.log(timeout(false)); // 调用Promise.reject() 返回promise 对象。
结果:
还有就是,如果async函数内部抛出错误, promise 对象有一个catch 方法进行捕获,如:
timeout(false).catch(err => {
console.log(err)
})
总结:async/await是解决异步的一个方式,其实现原理来自于Promise,所以,Promise才是重点!
而await的作用就是在async(异步)函数中等待一个promise的返回,==>> 个人理解,在async中实现同步,但await执行的却是异步!
伪代码:==>> 便于理解
第一种情况:单个Promise
async loadData(){
//调用接口,获取数据(接口是网上找的,不一定有用)
fetch('https://www.baidu.com/search/error.html') // 返回一个Promise对象
.then((res)=>{
//接口调用正常,将调用Promise.resolve()
return res.text() // res.text()是一个Promise对象
})
.then((res)=>{
//接口调用正常,将调用Promise.reject()
console.log(res) // res是最终的结果
})
}
第二种情况:多个Promise ==>> 容易造成 “回调地狱”
“回调地狱”:由于回调函数或者网络请求是异步的,在上面的代码中每一层的回调函数都需要依赖上一层的回调执行完,所以形成了层层嵌套的关系最终形成类似上面的回调地狱,但代码以此种形式展现时无疑是不利于我们阅读与维护的。
async loadData(){
//调用接口,获取数据(接口是网上找的,不一定有用)
fetch('https://www.baidu.com/search/error.html') // 返回一个Promise对象
.then((res)=>{
//接口调用正常,将调用Promise.resolve()
return res.text() // res.text()是一个Promise对象
fetch(url) //网络请求2或回调2 ==>> 第二个Promise
.then((res)=>{
.... //网络请求n或回调n ==>> 第n个Promise
})
.catch((err)=>{})
})
.catch((err)=>{
//接口调用正常,将调用Promise.reject()
console.log(err) // res是最终的结果
})
}
总结:在上面这种情况中,在一个Promise中还有多个其他的Promise,里面的Promise就必须依赖于上一次的回调执行完成才可以继续执行(需要注意的是:Promise本身是异步的哦)。这样的写法代码会很多,并且不好维护,而且容易造成回调地狱。这种时候await就比较好用了。
使用await:
async loadData(){
//调用接口,获取数据(接口是网上找的,不一定有用)
let data1 = await ...; //调用接口1
let data2 = await ...; //调用接口2
let data3 = await ...; //调用接口3
console.log(data1,data2,data3);
}
在上面这种情况中:loadData()函数是异步的,并且每一个接口的调用都是异步的,但是每一次调用接口的时候都使用了await,使用它会等待第一个接口调用完成之后,在调用第二个接口,之后在调用第三个接口,相当于在loadData()这个异步函数里面的各个请求接口获取数据直接 是同步的,但每一个请求接口的内部确实异步的。这样就没有使用.then(()=>{}).catch(()=>{})这样的写法了,await是比较好维护并且好理解的。
最后,看一下 async/await 在react-native中的使用:
方式一:async定义函数,在其余地方调用
方式二:直接对 componentWillMount 等生命周期方法使用async