一、同步代码和异步代码

同步代码:

逐行执行,需原地等待结果后,才继续往下执行

异步代码:

调用后耗时,不阻塞代码继续执行,在将来完成后触发回调函数

例如:定时器,事件,XHR...,接受异步代码的结果:回调函数

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>同步代码和异步代码</title>
</head>

<body>
    <h2>同步代码和异步代码</h2>
    <script>
        // 同步和异步代码,异步执行
        //----------- 1---直接输出:1,2
        console.log(1)
        const num = 1 + 1
        console.log(num)

        //---------- 2---直接输出:1,3 点击之后:2
        console.log(1)
        document.querySelector('h2').addEventListener('click', function() {
            console.log(2)

        })
        console.log(3)



        // ---------3---直接输出:1,3  一秒之后:2
        console.log(1)
        setTimeout(() => {
            console.log(2)

        }, 0)
        console.log(3)

        // ----------4--直接输出:1,3  响应回来:2
        console.log(1)
        const xhr = XMLHttpRequest()
        xhr.open('get', 'xxx')
        xhr.addEventListener('loadend', () => {
            console.log(2)

        })
        xhr.send()
        console.log(3)
    </script>
</body>

</html>

二、回调函数地狱

回调函数地狱: 在回调函数中调用回调函数,形成的代码结构称之为回调函数地狱
缺点: 可读性差,异常捕获困难
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>回调函数地狱</title>
</head>

<body>
    <h2>回调函数地狱</h2>
    <form>
        <span>省份:</span>
        <select>
      <option class="province"></option>
    </select>
        <span>城市:</span>
        <select>
      <option class="city"></option>
    </select>
        <span>地区:</span>
        <select>
      <option class="area"></option>
    </select>
    </form>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.3/axios.js"></script>
    <script src="../lodash.js"></script>
    <script>
        /**
         * 需求: 展示数据到下拉框中
         *  1. 获取省份数据并展示第1个省
         *  2. 获取第1个省的城市数据,并展示第1个城市
         *  3. 获取第1个城市的区数据,并展示第1个区
         * */


        //  回调函数地狱: 在回调函数中调用回调函数,形成的代码结构称之为回调函数地狱
        //  缺点: 可读性差,异常捕获困难

        axios({
            url: 'https://hmajax.itheima.net/api/province',

        }).then(res => {
            // console.log(res.data.list)
            const pname = res.data.list[0]
            document.querySelector('.province').innerHTML = pname
                // ------请求省已经成功→用这个省请求市---
            axios({

                url: 'https://hmajax.itheima.net/api/city',
                params: {
                    pname: pname,
                }
            }).then(res => {
                const cname = res.data.list[0]
                document.querySelector('.city').innerHTML = cname
                    // ------请求市已经成功→用这个市请求区县

                axios({
                    url: 'https://hmajax.itheima.net/api/area',
                    params: {
                        pname,
                        cname
                    }
                }).then(res => {
                    console.log(res)
                    document.querySelector('.area').innerHTML = res.data.list[0]

                })
            })
        })
    </script>

</body>

</html>

三、Promise-链式调用

Promise链式调用:
每一个then方法还会返回一个新生成的promise对象,这个对象可被用作链式调用
then方法的返回值:
then方法中的回调函数的返回值,会影响新生成的Promise对象最终状态和结果
作用:解决回调函数嵌套(回调函数地狱
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise-链式调用</title>
</head>

<body>
    <h2>Promise-链式调用</h2>
    <script>
        /**
         * Promise-链式调用
         *  1. then方法会返回一个新生成的promise对象,可以被链式调用
         *  2. then方法中回调函数返回值会影响新Promise对象的状态和结果
         * */
        function getPname() {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('省份数据')
                }, 2000)
            })
        }

        function getCname() {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('城市数据')
                }, 2000)
            })
        }

        // Promise链式调用的作用--解决回调函数嵌套(回调函数地狱)

        // Promise链式调用: 每一个then方法还会返回一个新生成的promise对象,这个对象可被用作链式调用
        // then方法的返回值: then方法中的回调函数的返回值,会影响新生成的Promise对象最终状态和结果

        //前一个then里面return后一个then要用的对象


        // /城市的数据要基于省份数据执行
        const p = getPname()

        p.then(res => {

            console.log(res)
            return getCname() //这里的return对象,为了的下一个then使用

        }).then(res => {
            // /这个then要执行,需要在前一个then里面return一个Promise对象
            console.log(res)

        })
    </script>
</body>

</html>

1,解决回调函数地狱

核心步骤: then的回调函数中返回Promise对象

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise-链式调用-解决回调地狱</title>
</head>

<body>
    <h2>Promise-链式调用-解决回调地狱</h2>
    <form>
        <span>省份:</span>
        <select>
      <option class="province"></option>
    </select>
        <span>城市:</span>
        <select>
      <option class="city"></option>
    </select>
        <span>地区:</span>
        <select>
      <option class="area"></option>
    </select>
    </form>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.2/axios.min.js"></script>
    <script>
        /**
         * 需求: 展示数据到下拉框中(Promise链式编程)
         *  1. 获取省份数据并展示第1个省
         *  2. 获取第1个省的城市数据,并展示第1个城市
         *  3. 获取第1个城市的区数据,并展示第1个区
         * */
        //  Promise链式调用--解决回调函数嵌套(回调函数地狱)
        let pname = ''
        axios({
                url: 'https://hmajax.itheima.net/api/province',

            }).then(res => {
                // console.log(res.data.list)
                pname = res.data.list[0]
                document.querySelector('.province').innerHTML = pname
                    // ------请求省已经成功→用这个省请求市---

                return axios({

                    url: 'https://hmajax.itheima.net/api/city',
                    params: {
                        pname: pname,
                    }
                })

            })
            .then(res => {
                const cname = res.data.list[0]
                document.querySelector('.city').innerHTML = cname
                    // ------请求市已经成功→用这个市请求区县
                return axios({
                    url: 'https://hmajax.itheima.net/api/area',
                    params: {
                        pname,
                        cname
                    }
                })

            })
            .then(res => {
                console.log(res)
                document.querySelector('.area').innerHTML = res.data.list[0]
            })

    </script>

</body>

</html>

四、async函数和await 

语法: 在async函数内,使用await关键字取代then函数,等待获取Promise对象成功状态的结果值

1,async函数和await-错误捕获

try {

// 需要被执行的语句
} catch (error) {
// error 接收错误信息
// try 有错误时执行的语句

}

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>async函数和await-错误捕获</title>
</head>

<body>
    <h2>async函数和await-错误捕获</h2>
    <form>
        <span>省份:</span>
        <select>
      <option class="province"></option>
    </select>
        <span>城市:</span>
        <select>
      <option class="city"></option>
    </select>
        <span>地区:</span>
        <select>
      <option class="area"></option>
    </select>
    </form>
    <!-- <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.3/axios.js"></script> -->
    <script src="../axios.min.js"></script>
    <script>
/**
         * 需求: 展示数据到下拉框中(async和await)
         *  1. 获取省份数据并展示第1个省
         *  2. 获取第1个省的城市数据,并展示第1个城市
         *  3. 获取第1个城市的区数据,并展示第1个区
         * */
        //  注意点1: 使用async关键字来声明函数
        // async异步 是个函数  await等待

        // 语法: 在async函数内,使用await关键字取代then函数,等待获取Promise对象成功状态的结果值
         //async函数和await
        async function func() {

            try {
                // 省份  await等待
                const res = await axios({
                    url: 'https://hmajax.itheima.net/api/province',

                })
                const pname = res.data.list[0]
                document.querySelector('.province').innerHTML = pname


                //请求城市
                const res1 = await axios({
                    url: 'https://hmajax.itheima.net/api/city',
                    params: {
                        pname: pname,
                    }
                })
                const cname = res1.data.list[0]
                document.querySelector('.city').innerHTML = cname


                // 请求区县
                const res2 = await axios({
                    url: 'https://hmajax.itheima.net/api/area',
                    params: {
                        pname,
                        cname
                    }
                })
                console.log(res2)
                document.querySelector('.area').innerHTML = res2.data.list[0]
            } catch (error) {
                console.log(`11`, error)
                    // try,catch
                    // 使用 async/await 关键字就可以在异步代码中使用普通的 try/catch 代码块。
                try {
                    // 需要被执行的语句
                } catch (error) {
                    // error接收错误信息
                    //try有错误时执行的语句
                }
            }

        }
        func()
        console.log(2222)
    </script>
</body>

</html>

五、事件循环-eventloop

JavaScript是单线程(代码逐行执行)的,

为了不让耗时代码阻塞其他代码运行,就设计了事件循环

<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>事件循环-练习</title>
</head>

<body>
  <h2>事件循环-练习</h2>
  <script>
    console.log(1)
    setTimeout(() => {
      console.log(2)
    }, 0)
    setTimeout(() => {
      console.log(3)
    }, 0)
    function getProvince() {
      const xhr = new XMLHttpRequest()
      xhr.open('get', 'http://hmajax.itheima.net/api/province')
      xhr.addEventListener('loadend', () => {
        console.log(4)
      })
      xhr.send()
    }
    getProvince()
    document.addEventListener('click', () => {
      console.log(5)
    })
    console.log(6)

    // 打印结果 1 6 2 3 4 点击之后打印5
  </script>
</body>

</html>
<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>事件循环-经典面试题</title>
</head>

<body>
  <h2>事件循环-经典面试题</h2>
  <script>
    console.log(1)
    setTimeout(() => {
      console.log(2)
      const p = new Promise((resolve) => resolve(3))
      p.then(res => console.log(res))
    }, 0)
    const p = new Promise(resolve => {
      setTimeout(() => console.log(4), 0)
      resolve(5)
    })
    p.then(res => { console.log(res) })
    const p2 = new Promise(resolve => resolve(6))
    p2.then(res => console.log(res))
    console.log(7)

    // 输出: 1 7 5 6 2 3 4
  </script>
</body>

</html>

 事件循环的模型(执行过程)

调用栈执行同步代码,异步代码交给宿主环境执行
异步代码等待时机成熟,送入任务队列排队(宏任务,然后是微任务
调用栈空闲时,反复查看并调用任务队列里的回调函数

异步任务(宏任务,微任务

1. (宏)任务: 浏览器环境执行的异步代码:
script标签,定时器,AJAX请求完成事件,用户交互事件等
2. 微任务: JS引擎环境执行的异步代码:
Promise对象.then和catch的回调

六、Promise.all 静态方法

 作用:

将多个Promise对象包装成一个新的Promise对象,获取所有的成功结果,或某一个的失败原因

 七、例

1,商品分类

//发一级分类的请求,可以得到一级分类的id,用一级分类的id当做查询参数,发二级分类的请求
//二级分类有4个→Promise.al1([p1,p2,p3.....])
//const res = axios()→async(函数)await---(省略了.then拿结果,如果不这样写就需要回调函数的地狱套用)

// 语法: 在async函数内,使用await关键字取代then函数,等待获取Promise对象成功状态的结果值


// 2. Promise.all 获取所有的成功结果,或者失败原因(第一个)



//async 把原来同步的一行一行按顺序执行的代码,改为异步的代码,各自执行各自的同时进行执行 
//await 等待各自执行的过程都结束了之后返回结果,
// 可以不通过.then拿结果
async function fn() {

    //获取一级分类的数据
    const res1 = await axios({
        url: 'https://hmajax.itheima.net/api/category/top',
    })

    console.log("yijiS", res1.data.data)

    //整合二级请求对象的数组→all([])
    const arr = res1.data.data.map(item => {
        return axios({
            url: 'https://hmajax.itheima.net/api/category/sub',
            params: {
                id: item.id
            }
        })

    })
    console.log('二级:', arr)
    const p = Promise.all(arr)
    console.log('二级p:', p)

    p.then(res => {
                console.log(res)
                let str = res.map(item => {
                            const { name, children } = item.data.data
                            return `
                            <div class="item">
                                <h3>${name}</h3>
                                <ul>
                                    ${children.map(item=>{
                                        return`
                                                <li>
                                                    <a href="javascript:;">
                                                    <img src="${item.picture}" />
                                                    <p>${item.name}</p>
                                                    </a>
                                                </li>
                                                `   
                                        }).join('')}
                                </ul>
                            </div>
                                    `


            
        }).join('')
        document.querySelector('.sub-list').innerHTML = str
    })
}

fn()

2,学习反馈

需求:1. 省份列表 2. 城市列表 3. 地区列表 4. 反馈提交
下拉框select,option下拉框的选项

 主要代码

// /省市区三级联动
//1.默认发请求得到省份数据-渲染页面
async function fn() {
    let res1 = await axios({
            url: 'https://hmajax.itheima.net/api/province',
        })
        // console.log(`1234567890-`, res1.data.list)
    console.log(`1234567890-`, res1)
    document.querySelector('.province').innerHTML = `<option value="">省份</option>` + res1.data.list.map(item => {
        return `<option value="${item}">${item}</option>`
    }).join('')


    //2.省份的值(value)发生变化了(change),请求这个省的所有市→渲染页面
    let pname = ''
    document.querySelector('.province').addEventListener('change', async function() {
        pname = this.value
        const res2 = await axios({
            url: 'https://hmajax.itheima.net/api/city',
            params: {
                pname
            }
        })

        console.log(res2.data.list)
        document.querySelector('.city').innerHTML = `<option value="">城市</option>` + res2.data.list.map(item => {
            return `<option value="${item}">${item}</option>`
        }).join('')
        document.querySelector('.area').innerHTML = `<option value="">地区</option>`
    })


    let cname = ''
        //3.市的值发生变化了,请求这个市的所有区县→渲染页面
    document.querySelector('.city').addEventListener('change', async function() {
        cname = this.value
        const res3 = await axios({
            url: 'https://hmajax.itheima.net/api/area',
            params: {
                pname,
                cname
            }
        })
        console.log(res3.data.list)
        document.querySelector('.area').innerHTML = `<option value="">地区</option>` + res3.data.list.map(item => {
            return `<option value="${item}">${item}</option>`
        }).join('')

    })
}
fn()
    //提交数据


document.querySelector('.submit').addEventListener('click', async function() {
    //收集数据
    const data = serialize(document.querySelector('.info-form'), { hash: true, empty: true })
        // 接口发请求提交
    const res = await axios({
        url: 'https://hmajax.itheima.net/api/feedback',
        method: 'post',
        data
    })
    alert(res.data.message)
})

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐