Ajax异步编程和事件循环

一.async和await

1.async和await语法

2.async和await注意事项

二.使用async和await优化回调地狱

三.EventLoop事件循环

四.微任务和宏任务

1.异步任务

2.微任务队列和宏任务队列

五.个人信息修改模板(附带头像上传)

六.自测题Ajax


一.async和await

1.async和await语法

async function 函数名() {
    const result = await Promise对象
    // 拿到Promise对象内成功的结果继续向下执行
}

示例: 


// 用await取代then函数, 来提取成功的值在原地
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功的值')
    }, 2000)
})

// 普通函数: async+await
async function fn() {
    const result = await p
    console.log(result)
}
fn()

// 箭头函数: async+await
const myFn = async () => {
    const result = await p
    console.log(result)
}
myFn()

async和await的作用是:取代then函数, 来提取Promise的值

2.async和await注意事项

await 必须用在async修饰的函数内

// 1. await必须用在被async修饰的函数内, 函数内无await, 则async可省略
// 报错: await is only valid in async functions
function myFn() {
    const res = await 1
}
myFn()

async修饰后, 此函数为异步函数

 // 在此函数内, 遇到await会暂停代码往下, 但不影响外面继续执行同步流程
// 等所有主线程同步代码走完, 再执行await并继续向下
console.log(1);
async function myFn() {
    console.log(2);
    const res = await 3
    console.log(res);
    console.log(4);
}
console.log(5);
myFn()
console.log(6);

await之后一般跟promise

await后面一般跟Promise对象
// 如果跟的是非Promise对象, 则等待所有同步代码执行后, 把此结果作为成功的结果留在原地使用
// 如果是Promise对象, 则等待Promise对象内触发resolve返回成功结果, 在原地使用
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    }, 2000)
})

console.log(1);
async function myFn() {
    console.log(2);
    const res = await p
    console.log(res);
    console.log(4);
}
console.log(5);
myFn()
console.log(6);

await不能捕获失败结果, 需要使用try+catch关键字捕获

/*
    try和catch语法
    try {
        // 这里放可能在执行中报错的代码
        // 如果报错会终止代码继续执行, 直接跳转进catch里执行
    } catch (err) {
        // err接收try大括号内报错抛出的异常代码
    }

*/
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        // resolve(1)
        reject(new Error('失败'))
    }, 2000)
})

async function myFn() {
    try {
        const res = await p
        console.log(res);
    } catch (err) {
        console.error(err)
    }
}
myFn()

二.使用async和await优化回调地狱

改进前

// 使用Promise的链式调用解决问题
// axios函数在原地返回的就是一个Promise对象
let pname = ''
axios.get('http://ajax-api.itheima.net/api/province').then(res => {
    // 2. 获取某个省, 对应的城市列表
    pname = res.data.data[5];
    return axios.get(`http://ajax-api.itheima.net/api/city?pname=${pname}`)
}).then(res => {
    // 3. 获取某个市, 对应的地区列表
    let cname = res.data.data[0]
    return axios.get(`http://ajax-api.itheima.net/api/area?pname=${pname}&cname=${cname}`)
}).then(res => {
    console.log(res);
})

改进后

// 使用Promise的链式调用解决问题
// axios函数在原地返回的就是一个Promise对象
async function f() {
  const provinces = await axios.get('http://ajax-api.itheima.net/api/province')
  const pname = provinces.data.data[5]
  const citys = await axios.get(`http://ajax-api.itheima.net/api/city?pname=${pname}`)
  const cname = citys.data.data[0]
  const areas = await axios.get(`http://ajax-api.itheima.net/api/area?pname=${pname}&cname=${cname}`)
	return areas
}
f()

三.EventLoop事件循环

浏览器中的EventLoop(这个比较偏理论一点,我是这样理解的):js是单线程的,一次只能做一件事。js在浏览器这个宿主环境中运行。浏览器是多线程的,用户交互,定时器,网络请求等等浏览器中的事件会产生对应的任务,任务多了要在任务队列中排队,浏览器的主线程依次取出任务来执行,此过程不断重复从而形成一个循环,称为EventLoop。

JavaScript 是一门单线程执行的脚本语言。也就是说,同一时间只能做一件事情。

javaScript要运行在宿主环境中(浏览器,nodejs)下。浏览器内部有执行js代码的引擎(V8引擎)

浏览器事件循环:

排队是任务是以事件及其回调的方式存在的。

当事件(用户的点击,图片的成功加载)发生时,将其回调添加到任务队列;主线程上的任务完成之后,就会从任务队列中取出任务来执行

四.微任务和宏任务

1.异步任务

不是马上执行,是放入到队列中等待;

如果所有的任务都要按序等待,那么也不行,需要有一个能插队的机制。所以又将异步任务分为微任务和宏任务,同时对应微任务队列和宏任务队列。

当主线程空闲时,先执行微任务队列中的任务,再去执行宏任务队列中的任务。

2.微任务队列和宏任务队列

微任务代码(js语法)

  • Promise对象.then()

宏任务代码(宿主环境)

  • script
  • dom事件
  • ajax
  • setTimout

五.个人信息修改模板(附带头像上传)

完成案例效果图:

 HTML和CSS代码:


<!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>08.案例_个人信息修改</title>
  <link rel="stylesheet" href="https://unpkg.com/bootstrap@5.1.3/dist/css/bootstrap.min.css" />
  <style>
    .form-select {
      width: 103px;
      display: inline-block;
    }

    .col-form-label {
      text-align: right;
    }

    .figure-img {
      width: 100px;
      height: 100px;
      cursor: pointer;
    }

    #upload {
      display: none;
    }
  </style>
</head>

<body>
  <div class="container">
    <h1 class="p-5">个人设置</h1>
    <form class="col-6">
      <div class="row mb-3">
        <label class="col-form-label col-3">昵称:</label>
        <div class="col-9">
          <input class="form-control col-9" type="text" name="nickname" />
        </div>
      </div>
      <div class="row mb-3">
        <label class="col-form-label col-3">籍贯:</label>
        <div class="col-9">
          <select class="form-select col-4" name="province">
            <option value="">--省--</option>
          </select>
          <select class="form-select col-4" name="city">
            <option value="">--市--</option>
          </select>
          <select class="form-select col-4" name="area">
            <option value="">--区--</option>
          </select>
        </div>
      </div>
      <div class="row mb-3">
        <label class="col-form-label col-3">头像:</label>
        <div class="col-9">
          <input class="form-control col-9" type="hidden" name="avatar" />
          <figure class="figure">
            <input type="file" id="upload" />
            <img src="https://yanxuan-item.nosdn.127.net/12a882699bd531a1bd428bffe1989525.jpg"
              class="figure-img img-fluid rounded" alt="..." />
            <figcaption class="figure-caption">修改头像</figcaption>
          </figure>
        </div>
      </div>
      <div class="row mb-3">
        <label class="col-3"></label>
        <div class="col-9">
          <button class="btn btn-primary">保存</button>
        </div>
      </div>
    </form>
  </div>

  <script src="https://unpkg.com/bootstrap@5.1.3/dist/js/bootstrap.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/axios@0.27.2/dist/axios.min.js"></script>
  <script src="./lib/form-serialize.js"></script>
 
</body>

</html>

落地js代码:

  //渲染个人信息
  <script>
        axios.defaults.baseURL = 'http://ajax-api.itheima.net'

        // 封装函数document.querySelector('#' + id)
        function $(id) {
            return document.querySelector('#' + id)
        }

        async function getInfo() {
            const res = await axios.get('/api/settings')
            const obj = res.data.data
            const { area, avatar, city, nickname, province } = obj
            // console.log(obj);

            // 数据回填
            $('avatar').src = avatar
            $('nickname').value = nickname

            // 把省的数据整体填入
            const resProvince = await axios.get('/api/province')
            $('province').innerHTML += resProvince.data.data.map(item => `<option value="${item}">${item}</option>`)
            $('province').value = province


            // 把市的数据整体填入
            const resCity = await axios.get(`/api/city?pname=${province}`)
            $('city').innerHTML += resCity.data.data.map(item => `<option value="${item}">${item}</option>`)
            $('city').value = city


            // 把地区的数据整体填入
            const resArea = await axios.get(`/api/area?pname=${province}&cname=${city}`)
            $('area').innerHTML += resArea.data.data.map(item => `<option value="${item}">${item}</option>`)
            $('area').value = area

        }
        getInfo()


        // 给省这个下拉框添加change事件
        $('province').addEventListener('change', async function () {
            // 获取最新选择的省
            const province = $('province').value
            // 发请求,得到对应的市
            const resCity = await axios.get(`/api/city?pname=${province}`)
            // console.log(resCity);
            // 添加(渲染)到页面
            $('city').innerHTML = resCity.data.data.map(item => `<option value="${item}">${item}</option>`)
            // 默认选择第一个市
            const city = resCity.data.data[0]
            $('city').value = city

            // 发请求,得到对应的地区
            const resArea = await axios.get(`/api/area?pname=${province}&cname=${city}`)
            // console.log(resArea);
            // 添加(渲染)到页面
            $('area').innerHTML = resArea.data.data.map(item => `<option value="${item}">${item}</option>`)
            $('area').value = resArea.data.data[0]
        })


        // 给市这个下拉框添加change事件
        $('city').addEventListener('change', async function () {
            // 获取最新选择的省
            const province = $('province').value
            // 拿到最新的市
            const city = $('city').value
            // 发请求,得到对应的市
            // console.log(city);

            // 发请求,得到对应的地区
            const resArea = await axios.get(`/api/area?pname=${province}&cname=${city}`)
            // console.log(resArea);
            // 添加(渲染)到页面
            $('area').innerHTML = resArea.data.data.map(item => `<option value="${item}">${item}</option>`)
            $('area').value = resArea.data.data[0]
        })


        // 修改图像

        // 给修改头像img注册点击事件
        $('avatar').addEventListener('click', function () {
            $('upload').click()
        })


        $('upload').addEventListener('change', async function (e) {
            let file = e.target.files[0]
            let fd = new FormData()
            fd.append('avatar', file)

            const portrait = await axios({
                url: '/api/file',
                method: 'POST',
                data: fd
            })
            console.log(portrait);

            $('avatar').src = portrait.data.data.url
        })


        // 保存按钮
        $('btnSave').addEventListener('click', async function (e) {
            // 阻止默认行为
            e.preventDefault()
            const nickname = $('nickname').value
            const province = $('province').value
            const city = $('city').value
            const area = $('area').value
            const avatar = $('avatar').src

            try {
                await axios.put('/api/settings', {
                    nickname,
                    province,
                    city,
                    area,
                    avatar
                })

                alert('YES')
            } catch (err) {
                alert('NO')
            }
        })
    </script>

任何需求的思路是:先标签后样式, 再数据铺设/绑定事件, 根据画面拆分需求按步骤实现, 前端准备好就调用接口

六.自测题Ajax

这里有10道Ajax选择题(祝你得100分): 异步编程和事件循环

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员大臣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值