JS第二十五次笔记

14 阅读7分钟

1 目录

image.png

1.1 同步代码和异步代码

image.png

1.1.1 同步代码

同步代码;逐行执行,需原地等待结果后,才继续向下执行。

1.1.2 异步代码

异步代码:调用后耗时,不阻塞代码继续执行(不必原地等待),在将来完成后触发一个回调函数

1.1.3 案例——同步异步执行顺序

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <button class="btn">我是按钮</button>
  <script>
    // 以下代码执行顺序是1、4、2、3(若三不点击,则执行顺序是1、4、2)
    const result = 0 + 1
    console.log(result)

    setTimeout(() => {
      console.log(2)
    }, 2000)

    document.querySelector('.btn').addEventListener('click', () => {
      console.log(3)
    })

    document.body.style.backgroundColor = 'pink'
    console.log(4)
  </script>
</body>

</html>

结果:

image.png

1.2 回调函数地狱和promise链式调用

1.2.1 回调函数地狱

1.2.1.1缺点:可读性差,异常无法获取,耦合性严重,牵一发动全身

回调地狱:回调函数(回调函数为一个异步任务)嵌套回调函数

image.png

<!DOCTYPE html>
<html lang="en">

<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>
  <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.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:演示回调函数地狱
     * 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
     * 概念:在回调函数中嵌套回调函数,一直嵌套下去就形成了回调函数地狱
     * 缺点:可读性差,异常无法获取,耦合性严重,牵一发动全身
    */

    axios({
      url: 'http://hmajax.itheima.net/api/province',
      method: 'GET',
    }).then(res => {
      // 根据省份id获取市
      const pname = res.data.list[0]
      // 省份
      document.querySelector('.province').innerHTML = pname
      // 根据省份获取城市
      axios({
        url: 'http://hmajax.itheima.net/api/city',
        method: 'GET',
        params: { pname }
      }).then(res => {
        const cname = res.data.list[0]
        document.querySelector('.city').innerHTML = cname
        // 根据省份和城市获取地区
        axios({
          url: 'http://hmajax.itheima.net/api/area',
          method: 'GET',
          params: { pname, cname }
        }).then(res => {
          document.querySelector('.area').innerHTML = res.data.list[0]
        })
      })
    })

  </script>
</body>

</html>

1.2.2 promise-链式调用

1.2.2.1 promise回顾:

Promise 是 JavaScript 中处理异步操作的一种解决方案,它代表一个异步操作的最终完成(或失败)及其结果值。Promise 对象有三个状态:

  1. pending(进行中):初始状态,既不是成功,也不是失败
  1. fulfilled(已成功):操作成功完成
  1. rejected(已失败):操作失败

Promise 的主要优点:

  1. 链式调用:可以连续调用 .then(),使代码更清晰
  1. 错误处理:可以使用 .catch() 统一处理错误
  1. 避免回调地狱:不需要嵌套多层回调函数

image.png

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    const p = new Promise((resolve, reject) => {
      resolve('成功1')
    })
    p
      .then(res => {
        console.log(res)
        return new Promise((resolve, reject) => {
          resolve(res + ' 成功2')
        })
      })
      // promise的链式调用:再写一个then接收上一个then的返回值
      .then(res => {
        console.log(res)
      })
  </script>
</body>

</html>

结果:

image.png

1.2.2.2 promise的链式调用可以解决回调地狱问题
<!DOCTYPE html>
<html lang="en">

<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>
  <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.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:把回调函数嵌套代码,改成Promise链式调用结构
     * 需求:获取默认第一个省,第一个市,第一个地区并展示在下拉菜单中
    */
  //  因为多个函数都要调用pname(省份名字),所以定义一个全局变量
    let pname = ''
    axios({
      url: 'http://hmajax.itheima.net/api/province',
      method: 'get'
    }).then(res => {
      pname = res.data.list[0]
      document.querySelector('.province').innerHTML = pname

      // axios的返回结果是一个promise对象,所以可以继续then
      return axios({
        url: 'http://hmajax.itheima.net/api/city',
        method: 'get',
        params: {
          pname: res.data.list[0]
        }
      })
    })
      // 再使用then接收上一次的返回结果
      .then(res => {
        document.querySelector('.city').innerHTML = res.data.list[0]
        return axios({
          url: 'http://hmajax.itheima.net/api/area',
          method: 'get',
          params: {
            params: pname,
            cname: res.data.list[0]
          }
        })
          .then(res => {
            document.querySelector('.area').innerHTML = res.data.list[0]
          })
      })
  </script>
</body>

</html>

1.3 async和await使用

前言:promise链式调用虽然可以解决回调地狱,但是代码量较大,代码冗杂 async和await关键字让我们可以使用一种更加简洁的方式写出基于promise的异步行为,而无需刻意的调用promise

1.3.1 async和await的使用方式

image.png

<!DOCTYPE html>
<html lang="en">

<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>
  <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.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:掌握async和await语法,解决回调函数地狱
     * 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
     * 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
    */
    //  async修饰函数,所以需要定义一个async函数

    async function render() {
      // await的等待结果会直接返回到变量中
      const res = await axios({
        url: 'http://hmajax.itheima.net/api/province',
        method: 'get',

      })

      document.querySelector('.province').innerHTML = res.data.list[0]
      const resCity = await axios({
        url: 'http://hmajax.itheima.net/api/city',
        method: 'get',
        params: {
          pname: res.data.list[0]
        }
      })
      document.querySelector('.city').innerHTML = resCity.data.list[0]

      const resArea = await axios({
        url: 'http://hmajax.itheima.net/api/area',
        method: 'get',
        params: {
          pname: res.data.list[0],
          cname: resCity.data.list[0]
        }
      })

      document.querySelector('.area').innerHTML = resArea.data.list[0]
    }
    render()
  </script>
</body>

</html>

结果:

image.png

1.3.2 async和await捕获错误

async和await只能解决成功的回调 本质只能使用.then而没有使用.catch的方法;所以会使用到“try,catch”捕获错误

image.png

<!DOCTYPE html>
<html lang="en">

<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>
  <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.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:掌握async和await语法,解决回调函数地狱
     * 概念:在async函数内,使用await关键字,获取Promise对象"成功状态"结果值
     * 注意:await必须用在async修饰的函数内(await会阻止"异步函数内"代码继续执行,原地等待结果)
    */
    //  async修饰函数,所以需要定义一个async函数

    async function render() {
      // await的等待结果会直接返回到变量中
      try {
        const res = await axios({
          url: 'http://hmajax.itheima.net/api/province',
          method: 'get',

        })

        document.querySelector('.province').innerHTML = res.data.list[0]
        const resCity = await axios({
          url: 'http://hmajax.itheima.net/api/city',
          method: 'get',
          params: {
            pname: res.data.list[0]
          }
        })
        document.querySelector('.city').innerHTML = resCity.data.list[0]

        const resArea = await axios({
          url: 'http://hmajax.itheima.net/api/area',
          method: 'get',
          params: {
            pname: res.data.list[0],
            cname: resCity.data.list[0]
          }
        })

        document.querySelector('.area').innerHTML = resArea.data.list[0]
      } catch (err) {
        console.log(err)

      }
    }
    render()
  </script>
</body>

</html>

1.4 事件循环——EventLoop

好处:掌握JavaScript是如何安排和运行代码的

image.png 定义:执行和收集异步任务的模型(机制),在调用栈空闲时,会反复调用任务队列里的回调函数的执行机制,叫做事件循环(因为JavaScript是单线程的,为了不阻塞JavaScript引擎,设计执行代码的模型) image.png

1.5 宏任务和微任务

image.png

先执行第一个宏任务(script 或者js文件), 如果宏任务中有微任务,那么在执行完当前宏任务之后,会优先执行当前宏任务的微任务;微任务执行完成后,继续执行下一个宏任务 image.png

1.6 promise.all 静态方法

image.png

image.png

<!DOCTYPE html>
<html lang="en">

<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的all方法</title>
</head>

<body>
  <ul class="my-ul"></ul>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:掌握Promise的all方法作用,和使用场景
     * 业务:当我需要同一时间显示多个请求的结果时,就要把多请求合并
     * 例如:默认显示"北京", "上海", "广州", "深圳"的天气在首页查看
     * code:
     * 北京-110100
     * 上海-310100
     * 广州-440100
     * 深圳-440300
    */
    const arr = [
      axios({
        url: 'http://hmajax.itheima.net/api/weather',
        method: 'get',
        params: {
          city: '110100'
        }
      }),
      axios({
        url: 'http://hmajax.itheima.net/api/weather',
        method: 'get',
        params: {
          city: '310100'
        }
      }),
      axios({
        url: 'http://hmajax.itheima.net/api/weather',
        method: 'get',
        params: {
          city: '440100'
        }
      }),
      axios({
        url: 'http://hmajax.itheima.net/api/weather',
        method: 'get',
        params: {
          city: '440300'
        }
      })
    ]

    // 等待机制=>all方法会等待所有的promise对象的成功返回结果才会执行then方法,一旦有一个失败了都会执行错误结果

    Promise.all(arr).then(resArr => {
      document.querySelector('.my-ul').innerHTML = resArr.map(item => {
        return `<li>${item.data.data.area}-----${item.data.data.weather}</li>`
      }).join('')
    }).catch(err => {
      console.log(err)
    })
  </script>
</body>

</html>

结果:

image.png

1.7 案例——省市区切换

1.7.1 数据获取和列表渲染

image.png

 /**
 * 目标1:完成省市区下拉列表切换
 *  1.1 设置省份下拉菜单数据
 *  1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
 *  1.3 切换城市,设置地区下拉菜单数据
 */
axios({
  url: 'http://hmajax.itheima.net/api/province'

}).then(res => {
  // console.log(res.data.list);
  // 将省份数据保存为一个变量
  const list = res.data.list
  // 拼接字符串 并且渲染到页面中
  document.querySelector('.province').innerHTML += list.map(item => {
    return `<option value="${item}">${item}</option>`
  }).join('')

})

// 切换省份,设置城市下拉菜单数据,清空地区下拉菜单(change的意思是当值改变的时候触发)
document.querySelector('.province').addEventListener('change', async e => {
  // 清空下拉菜单
  document.querySelector('.city').innerHTML = `<option value="">城市</option>`
  console.log(e.target.value);
  const { data: { list } } = await axios({
    url: 'http://hmajax.itheima.net/api/city',
    get: 'get',
    params: { pname: e.target.value }

  })
  console.log(list)
  document.querySelector('.city').innerHTML += list.map(item => `<option>${item}</option>`).join('')
})

// 切换市,设置区域的下拉菜单数据
document.querySelector('.city').addEventListener('change', async e => {
  // 地区列表去除先的列表数据
  document.querySelector('.area').innerHTML = `<option value="">地区</option>`
  // console.log(e.target.value)//只能获取到城市而不是省份
  // 如何获取省份
  // 不使用.then获取省份,就使用await和async
  const { data: { list } } = await axios({
    url: 'http://hmajax.itheima.net/api/area',
    method: 'get',
    params: {
      pname: document.querySelector('.province').value,
      cname: e.target.value
    }
  })
  // 渲染页面;
  document.querySelector('.area').innerHTML += list.map(item => {
    return `<option value="${item}">${item}</option>`
  }).join('')


})

1.7.2 数据提交

image.png

/**
 * 目标1:完成省市区下拉列表切换
 *  1.1 设置省份下拉菜单数据
 *  1.2 切换省份,设置城市下拉菜单数据,清空地区下拉菜单
 *  1.3 切换城市,设置地区下拉菜单数据
 */
axios({
  url: 'http://hmajax.itheima.net/api/province'

}).then(res => {
  // console.log(res.data.list);
  // 将省份数据保存为一个变量
  const list = res.data.list
  // 拼接字符串 并且渲染到页面中
  document.querySelector('.province').innerHTML += list.map(item => {
    return `<option value="${item}">${item}</option>`
  }).join('')

})

// 切换省份,设置城市下拉菜单数据,清空地区下拉菜单(change的意思是当值改变的时候触发)
document.querySelector('.province').addEventListener('change', async e => {
  // 清空下拉菜单
  document.querySelector('.city').innerHTML = `<option value="">城市</option>`
  console.log(e.target.value);
  const { data: { list } } = await axios({
    url: 'http://hmajax.itheima.net/api/city',
    get: 'get',
    params: { pname: e.target.value }

  })
  console.log(list)
  document.querySelector('.city').innerHTML += list.map(item => `<option>${item}</option>`).join('')
})

// 切换市,设置区域的下拉菜单数据
document.querySelector('.city').addEventListener('change', async e => {
  // 地区列表去除先的列表数据
  document.querySelector('.area').innerHTML = `<option value="">地区</option>`
  // console.log(e.target.value)//只能获取到城市而不是省份
  // 如何获取省份
  // 不使用.then获取省份,就使用await和async
  const { data: { list } } = await axios({
    url: 'http://hmajax.itheima.net/api/area',
    method: 'get',
    params: {
      pname: document.querySelector('.province').value,
      cname: e.target.value
    }
  })
  // 渲染页面;
  document.querySelector('.area').innerHTML += list.map(item => {
    return `<option value="${item}">${item}</option>`
  }).join('')


})


// 目标二:提交数据,显示结果
const infoFrom = document.querySelector('.info-form')
document.querySelector('.submit').addEventListener('click', async e => {
  // 使用插件收集表单数据,(要先在index里引入插件)
  // empty: true获取为空的数据
  const Data = serialize(infoFrom, { hash: true, empty: true })
  const res = await axios({
    url: 'http://hmajax.itheima.net/api/feedback',
    method: 'POST',
    data: Data

  })
  alert(res.data.message)
  // 提交成功后,清空表单数据
  infoFrom.reset()
})

结果:

image.png