前言:最近新开了个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('虽然在后面,但是我先执行');

上面代码得到的效果:

react axios触发了两次 react await async_react axios触发了两次

注意: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 对象。

结果:

react axios触发了两次 react await async_async/await_02

还有就是,如果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定义函数,在其余地方调用

react axios触发了两次 react await async_async/await_03

方式二:直接对 componentWillMount 等生命周期方法使用async

react axios触发了两次 react await async_async/await_04