一、异步问题

1.案例一 :模拟对用户信息的操作(注意:先获取用户列表后再对用户列表进行操作)

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        function getUserMeaasgeList() {
            //  发起请求获取用户列表
            axios.get(`http://1.12.253.99:3007/api/getUserMessage`).then(function(res) {
                // 打印用户列表数据
                console.log(res.data);
            })
            console.log('对获取的用户列表进行操作')
        }
        getUserMeaasgeList()
    </script>

应该是先执行axios里面的回调函数打印出‘用户列表数据‘后再打印‘对获取的用户列表进行操作’)

 接下来看代码运行:

在线axios执行 如何解决axios执行顺序_javascript

先执行打印‘对获取的用户列表进行操作’后再执行axios里面的回调函数打印出‘用户列表数据‘

 ①引起的问题:

用户列表没有获取到就对用户列表进行操作,在项目中代码会报错等,也不符合我们的项目业务需求(比如应该先购买商品后再生成订单等有顺序关系的逻辑)

②为什么代码执行顺序不对?

原因:实际上,js在执行过程中,每遇到一个异步函数,为了不影响主线程的整体代码运行时间(定时器函数比较耗费时间),都会将这个异步函数放入一个异步队列中,只有当主线程执行结束之后,才会开始执行异步队列中的函数。

典型的异步函数有:定时器的回调函数,axios和ajax的回调函数等

③代码实际运行

在线axios执行 如何解决axios执行顺序_vue.js_02

案例二:回调地狱问题 

可能大家会想:把业务逻辑代码都放一个回调函数中处理 ,就比如

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        function getUserMeaasgeList() {
            //  发起请求获取用户列表
            axios.get(`http://1.12.253.99:3007/api/getUserMessage`).then(function(res) {
                // 打印用户列表数据
                console.log(res.data);
                console.log('对获取的用户列表进行操作')
            })
        }
        getUserMeaasgeList()
    </script>

确实可以解决异步问题,但是这样会形成回调地狱问题

②试想一下,如果我在回调函数里面还要执行回调函数呢?

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        function getUserMeaasgeList() {
            //  发起请求获取用户列表
            axios.get(`http://1.12.253.99:3007/api/getUserMessage`).then(function(res) {
                // 打印用户列表数据
                console.log(res.data);
                // 对获取的用户列表进行操作
                axios.get(`...`).then(function(res) {
                    // 对获取的用户列表再进行操作
                    axios.get(`...`).then(function(res) {
                        // 对获取的用户列表再再进行操作
                        axios.get(`...`).then(function(res) {
                            // 对获取的用户列表再再再进行操作
                            axios.get(`...`).then(function(res) {})
                        })
                    })
                })
            })

        }
        getUserMeaasgeList()
    </script>

整体的维护性,可读性都变的极差,如果出了bug,修复的排查过程也变的极为困难,这个便是所谓的 回调函数地狱

 二、用async和await解决js的异步问题和回调地狱问题

    ES2017 标准引入了 async 和await关键字,使得异步操作跟同步操作一样,变得更加方便,这也是解决js异步问题回调地狱的最佳解决方案。

async关键字

①作为一个关键字放到函数前面,用于表示函数是一个异步函数,async就是异步的意思, 异步函数也就意味着该函数的执行不会阻塞后面代码的执行

async function timeout() {
    return 'hello world'
}
timeout();
console.log('虽然在后面,但是我先执行');

打开控制台运行: 

在线axios执行 如何解决axios执行顺序_vue.js_03

可以看到 :async 函数 timeout  调用了,但是没有任何输出, 看一看timeout()执行返回了什么? 把上面的 timeout() 语句改为console.log(timeout())

async function timeout() {
    return 'hello world'
}
console.log(timeout());
console.log('虽然在后面,但是我先执行');

再次运行打开控制台:

在线axios执行 如何解决axios执行顺序_在线axios执行_04

 

可以看出:

②async函数的返回值为promise对象,如果要获取到promise 返回值,应该用then 方法

继续修改代码:

async function timeout() {
    return 'hello world'
}
timeout().then(result => {
    console.log(result);
})
console.log('虽然在后面,但是我先执行');

运行代码控制台输出:

在线axios执行 如何解决axios执行顺序_前端_05

 

 

 

 

 

③async返回的promise对象的结果值由async函数执行的返回值决定

1.如果返回的是一个非Promise的对象,则fn()返回的结果就是成功状态的Promise对象,内部会调用Promise.solve() 方法把它转化成一个promise 对象作为返回,值为返回值,想获取返回值就调用then方法获取

<script>
        async function fn() {
            return 'async函数'
        }
        console.log(fn());
    </script>

在线axios执行 如何解决axios执行顺序_ecmascript_06

2.如果返回的是一个Promise对象,则fn()返回的结果与内部Promise对象的结果一致 ,想获取返回值就调用then方法获取

①返回的是一个Promise对象为成功状态,则函数fn返回结果内部Promise对象的结果一致 

<script>
        async function fn() {
            return new Promise((resolve, reject) => {
                resolve('成功的数据')
            })
        }
        console.log(fn());
    </script>

控制台运行: 

在线axios执行 如何解决axios执行顺序_ecmascript_07

 

 

② 返回的是一个Promise对象为失败状态,则函数fn返回结果内部Promise对象的结果一致 

 

<script>
        async function fn() {
            return new Promise((resolve, reject) => {
                reject('失败的数据')
            })
        }
        console.log(fn());
    </script>

控制台运行

在线axios执行 如何解决axios执行顺序_vue.js_08

 

④.如果函数内部抛出错误,就会调用Promise.reject() 返回一个失败状态的Promise对象

<script>
        async function fn() {
            throw '出异常问题了'
        }
        console.log(fn());
    </script>

控制台运行:

在线axios执行 如何解决axios执行顺序_ecmascript_09

 ⑤.如果函数内部抛出错误, promise 对象有一个catch 方法进行捕获。

<script>
        async function fn() {
            throw '出异常问题了'
        }
        fn().catch(err => {
            console.log(err);
        })
    </script>

控制台运行

 2.async总结

①作为一个关键字放到函数前面,用于表示函数是一个异步函数,async就是异步的意思, 异步函数也就意味着该函数的执行不会阻塞后面代码的执行

②async函数的返回值为promise对象(拥有promise的全部特性),如果要获取到promise 返回值,应该用then 方法

③async返回的promise对象的结果值由async函数执行的返回值决定

3.await关键字

①await必须放在async函数中

②await右侧的表达式可以放任何表达式,一般为promise对象

③await可以返回的是右侧promise成功的值

④await右侧的promise如果失败了,就会抛出异常,需要通过catch捕获处理

⑤await的意思是等待的意思,当主线程执行到await后面的表达式的时候,主线程会等待await后面的表达式运行结束后再继续运行主线程的其他代码(完美解决异步问题)

promise成功的值

1.  // 1创建promise对象
    const p = new Promise((resolve, rejiect) => {
        resolve('成功!')
    });
    // await 必须在async函数里面
    async function main() {
     //result接受promise成功的返回值
        let result = await p;
        console.log(result);//成功
    }
    main();

2.promise状态为失败,通过catch捕获处理

<script>
        1. // 1创建promise对象
        const p = new Promise((resolve, rejiect) => {
            rejiect('失败!')
        });
        // await 必须在async函数里面
        async function main() {
            let result = await p.catch(err => {
               捕获失败的结果//
                console.log(err);//失败
            });
            console.log(result);
        }
        main();
    </script>

三、解决本文章的异步问题和回调地狱问题

1.异步问题:(axios方法的返回值就是promise对象,所以可以用async和await来处理)

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        async function getUserMeaasgeList() {
            //  发起请求获取用户列表
            const result = await axios.get(`http://1.12.253.99:3007/api/getUserMessage`)
            console.log(result.data);
            console.log('对获取的用户列表进行操作')
        }
        getUserMeaasgeList()
    </script>

 控制台运行:

在线axios执行 如何解决axios执行顺序_ecmascript_10

2.回调地狱问题:

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        async function getUserMeaasgeList() {
            //  发起请求获取用户列表
            const result1 = await axios.get(`http://1.12.253.99:3007/api/getUserMessage`)
                //对用户列表进行操作
            const result2 = await axios.get(``)
                //对用户列表再进行操作
            const result3 = await axios.get(``)
                //对用户列表再再进行操作
            const result4 = await axios.get(``)
        }
        getUserMeaasgeList()
    </script>

四.总结

1.异步问题:实际上,js在执行过程中,每遇到一个异步函数,为了不影响主线程的整体代码运行时间(定时器函数比较耗费时间),都会将这个异步函数放入一个异步队列中,只有当同步线程执行结束之后,才会开始执行异步队列中的函数。

典型的异步函数有:定时器的回调函数,axios和ajax的回调函数等

2.ES2017 标准引入了 async 和await关键字,使得异步操作跟同步操作一样,变得更加方便,这也是解决js异步问题和回调地狱的最佳解决方案。