js中的ajax【Axios,XMLHttpRequest,Promise,async】回调函数地狱等问题

目录

前置知识

1.什么是异步请求?

2.什么是回调函数

3.如何查看网页的异步请求(XHR)?

4.什么是ajax

jquery的ajax,xhr,axios关系

正文---几种请求之间的关系

axios

Axios的诞生

Axios的介绍

定义

原理

◼️ Axios的基础用法

代码

XMLHttpRequest

XHR(XMLHttpRequest)是什么?

代码

基础用法

get方法

post方法

promise

定义 

代码 

封装简易的axios

什么是回调函数地狱

回调函数地狱示例 

使用 Promises 体现回调函数

Promise 链式调用

总结

用promise封装的axios解决回调地狱

Promise.all 静态方法

代码 

async与awiat

定义

async/await 出现的原因

关于async

关于await

1) await 到底在等啥?

2) await 等到了要等的,然后呢?

综合应用


前置知识

1.什么是异步请求?

请求:客户端向服务器请求数据!

无异步请求:在完整网页上发送任何请求,都会导致整个页面的全部刷新!

有异步请求:可以实现网页的局部刷新!

2.什么是回调函数

想象一下,你在一个餐厅里点菜。你告诉服务员你想要什么菜,然后你就坐在那里等。服务员去厨房告诉厨师你的点单,厨师开始准备你的菜。当菜准备好后,服务员会端给你。在这个过程中,服务员就像是回调函数。

  • :发起请求的人(调用者)
  • 服务员:回调函数
  • 厨房:执行任务的地方(可能是异步操作,比如网络请求)
  • :任务的结果

这里是一个简化的例子:

  1. 你(调用者)告诉服务员(回调函数),你想要一份炒饭。
  2. 服务员去厨房(执行任务的地方),告诉厨师你点了炒饭。
  3. 厨师开始准备炒饭(执行异步操作)。
  4. 炒饭准备好后,厨师让服务员端给你(回调函数被调用)。

在JavaScript中,这可以写成如下代码:

javascript
function 点菜(菜名, 服务员) {console.log(`点菜:${菜名}`);setTimeout(() => {console.log(`菜做好了:${菜名}`);服务员(菜名); // 当菜准备好了,服务员把菜端给你}, 1000); // 假设厨师需要1秒钟来准备菜
}function 服务员端菜(菜名) {console.log(`服务员端菜:${菜名}`);
}点菜('炒饭', 服务员端菜);

在这个例子中:

  • 点菜 函数代表发起请求。
  • 服务员端菜 函数是回调函数,它在菜准备好后被调用。
  • setTimeout 模拟了异步操作,比如网络请求或者文件读写等。

3.如何查看网页的异步请求(XHR)?

F12=>Network=>XHR,刷新页面我们可以看到网页所有的XHR(XMLHttpRequest)请求

4.什么是ajax

—— 异步网络请求 —— Ajax能够让页面无刷新的请求数据 ——

AJAX 不是新的编程语言,而是一种使用现有标准的新方法。

AJAX 是与服务器交换数据并更新部分网页的艺术,在不重新加载整个页面的情况下 ;通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新

实现ajax的方式有多种,如jQuery封装的ajax,原生的XMLHttpRequest,以及axios都可以实现异步网络请求。

jquery的ajax,xhr,axios关系

Ajax是一种技术方案,但并不是一种新技术。它依赖现有的CSS/HTML/JavaScript,而其中最核心的依赖是浏览器提供的XMLHttpRequest对象,是这个对象使得浏览器可以发出HTTP请求与接收HTTP响应。实现了在页面不刷新个情况下和服务器进行数据交互。 异步的javascript和xml AJAX 是一种用于创建快速动态网页的技术。 ajax用来与后台交互

所以,我们现在梳理一下三者之间的关系:

① Ajax的实现依赖于XMLHttpRequest对象,即XMLHttpRequest可以实现Ajax。

Asynchronous JavaScript + XML(异步JavaScript和XML), 其本身不是一种新技术,而是一个在 2005年被Jesse James Garrett提出的新术语,用来描述一种使用现有技术集合‘新’方法,包括: HTML 或 XHTML, CSS,JavaScript, DOM, XML, XSLT, 以及最重要的 XMLHttpRequest。当使用结合了这些技术的AJAX模型以后, 网页应用能够快速地将增量更新呈现在用户界面上,而不需要重载(刷新)整个页面。这使得程序能够更快地回应用户的操作。

XMLHttpRequest是AJAX的基础,XMLHttpRequest API是Ajax的核心。XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

② Axios在此基础上封装了XMLHttpRequest,即Axios可以实现Ajax

③ Jquery是对Javascript的一种轻量级封装的框架,而Ajax是JavaScript的一种应用,是异步JavaScript和XML——由XML+Javascript组合起来的一种异步请求技术,可实现动态局部刷新。也就是说Jquey是JavaScript的一个函数库,而JavaScript包含Ajax。 Jquery在原生Ajax的基础上进行了封装(说白了Jquey封装了Ajax,其实就是对原生​​XHR​​的封装——做了兼容处理,简化了使用),也就是说在Jquery中可以用Ajax。

JQuery 提供了用于 AJAX 开发的丰富函数(方法)库。 通过 jQuery Ajax,使用 HTTP Get 和 HTTP Post,你都可以从远程服务器请求 TXT、HTML、XML 或 JSON。

但各种方式都有利弊:

原生的XMLHttpRequest的配置和调用方式都很繁琐,实现异步请求十分麻烦。

jQuery的ajax相对于原生的ajax是非常好用的,但是没有必要因为要用ajax异步网络请求而引用整个jQuery框架。

正文---几种请求之间的关系

axios

Axios的诞生

为什么会诞生Axios?说到Axios我们就不得不说下Ajax。最初的旧浏览器页面在向服务器请求数据时,由于返回的是整个页面的数据,所以整个页面都会强制刷新一下,这对于用户来讲并不是很友好。因为当我们只需要请求部分数据时,服务器返回给我们的确是整个页面的数据,这样会造成网络资源的占用,即十分消耗网络资源。为了提高数据请求效率,异步网络请求Ajax就应运而生了,它可以在页面无刷新的情况下请求数据。因此,这样一来,当我们只是需要修改页面的部分数据时,可以实现不刷新页面的功能。

Axios的介绍

定义

Axios是一个基于promise 的 HTTP 库(类似于jQuery的Ajax,用于HTTP请求),可以用在浏览器和 node.js中(既可以用于客户端也可以用于node.js编写的服务端)。

Axios 是一个基于 promise 的网络请求库,可以用于浏览器和 node.js中。Axios(相比于原生的XMLHttpRequest对象来说) 简单易用,(相比于jQuery)axios包尺寸小且提供了易于扩展的接口,是专注于网络请求的库。

原理

axios(ajax i/o system)不是一种新技术,本质上也是对原生XHR(XMLHttpReques)的封装,只不过它是基于Promise的,是Promise的实现版本,符合最新的ES规范。

◼️ Axios的基础用法

Axios常用的几种请求方法:get,post,put,patch,delete

get:一般用于获取数据

post:一般用于提交数据(表单提交与文件上传)

patch:更新数据(只将修改的数据推送到后端(服务端))

put:更新数据(所有数据推送到服务端)

delete:删除数据

题外话:一般公司在实际项目开发过程中:

(1) post:一般用于新建数据

(2) put:一般用于更新数据(适合数据量比较小的更新)

(3) patch一般用于数据量较大的时候的数据更新。

比如,一个表单的数据量很大,有很多项,使用put的话,全部数据推送一次是比较耗性能 的,这个时候可以考虑用patch,只将修改的数据推送到后端。

以上这些题外话,只是一般的常规用法,不能代表一定要这样用;当然了,你可能会说我用post来获取数据,行不行?这个当然行了,答案是肯定的,绝对没问题!具体怎么用还是前后端一起商量着决定。

代码

引入 axios.js 文件到自己的网页中(如果第一个加载不了,可以使用第二个

https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js

https://cdn.bootcdn.net/ajax/libs/axios/1.3.4/axios.min.js

// 1.使用axios和服务器通信axios({url: 'http://hmajax.itheima.net/api/login',method: 'POST',data: {username,// username: username,password// password: ''}}).then(res => {console.log(res.data.message)myAlert(res.data.message, true)}).catch(err => {console.log(err.response.data.message)myAlert(err.response.data.message, false)})
})

XMLHttpRequest

XHR(XMLHttpRequest)是什么?

XHR是浏览器提供的API,通过该API,简化了异步通信的过程。XHR可以同步或异步地返回 Web 服务器的响应,并且能够以文本或者一个 DOM 文档的形式返回内容。

代码

基础用法
   * */// 1. 创建 XMLHttpRequest对象const xhr = new XMLHttpRequest()// 2. 设置请求方法 和 请求URLxhr.open('GET', 'http://hmajax.itheima.net/api/province')// 3. 监听 loadend 事件,接收响应结果xhr.addEventListener('loadend', () => {// console.log(xhr.response)// JSON-->JS对象const data = JSON.parse(xhr.response)// console.log(data)// 渲染到页面上document.querySelector('.list').innerText = data.list})// 4. 发起请求xhr.send()

get方法
 /*** 需求: 根据省份和城市名字, 查询对应的地区列表*  1. 生成查询参数*  2. 调用接口*  3. 渲染数据*/document.querySelector('.sel-btn').addEventListener('click', () => {// 1. 生成查询参数// 1.1 获取输入的省市const pname = document.querySelector('.province').valueconst cname = document.querySelector('.city').valueconsole.log(pname, cname)// 1.2 通过 URLSearchParams 快速生成查询参数const params = new URLSearchParams({ pname, cname })// 中文会被编码,不影响服务器解析const query = params.toString()console.log(query)// 2. 调用接口// 2.1 实例化对象const xhr = new XMLHttpRequest()// 2.2 设置请求的方法和URL地址xhr.open('GET', `http://hmajax.itheima.net/api/area?${query}`)// 2.3 监听事件,接收并使用数据xhr.addEventListener('loadend', () => {// console.log(xhr.response)// 3. 渲染数据// 3.1 JSON-JS对象const data = JSON.parse(xhr.response)// console.log(data)// 3.2 渲染const html = data.list.map(v => `<li class="list-group-item">${v}</li>`).join('')// console.log(html)document.querySelector('.area-group').innerHTML = html})// 2.4 发送请求xhr.send()})
post方法
document.querySelector('.btn').addEventListener('click', () => {// 1. 实例化XHR对象const xhr = new XMLHttpRequest()// 2. 设置请求方法和地址xhr.open('POST', 'http://hmajax.itheima.net/api/register')// 3. 监听事件,接收并使用数据xhr.addEventListener('loadend', () => {console.log(xhr.response)})// 4. 设置请求头信息(根据接口文档设置Content-Type)xhr.setRequestHeader('Content-Type', 'application/json')// 5. 请求体发送数据(和请求头设置的一致)// 方案1: 自己写JSON// 方案2: JS对象--》转为JSON(推荐!)-JSON.stringifyconst obj = {username: 'itheima999',password: '123456'}const data = JSON.stringify(obj)xhr.send(data)})

promise

定义 

Promise 是 JavaScript 中用于异步编程的一个对象,它代表了异步操作的最终完成(或失败)及其结果值。Promise 的主要目的是提供一个更合理、更强大的异步编程模型,以替代传统的回调函数。

  1. 为什么要了解 Promise 的三种状态 ?
    • 知道 Promise 对象如何关联的处理函数,以及代码的执行顺序
  2. Promise 有哪三种状态?
  3. 每个 Promise 对象必定处于以下三种状态之一
    1. 待定(pending):初始状态,既没有被兑现,也没有被拒绝
    2. 已兑现(fulfilled):操作成功完成
    3. 已拒绝(rejected):操作失败

代码 

  /*** Promise的状态*  1. pending 待定(默认状态): 实例化*  2. fulfilled 已兑现(成功): resolve-》then*  3. rejected 已拒绝(失败) : reject-》catch* * Promise对象的状态发生改变之后,无法再次改变* */// 1. 实例化Promise对象 pending 待定(默认状态)const p = new Promise((resolve, reject) => {// 2. 执行异步操作,传递结果setTimeout(() => {// fulfilled 已兑现(成功)// resolve('哈哈 成功啦')// rejected 已拒绝(失败)reject('嘤嘤嘤 失败啦')// 状态改变之后,无法再次改变resolve('nice 我又成功啦')}, 1000);})//  3. 接收结果p.then(res => {console.log(res)}).catch(err => {console.log(err)})

封装简易的axios

 /*** 封装-简易axios-获取省份列表* */// 1. 定义 hmAxios 函数function hmAxios(config) {console.log(config)// 2. 返回Promise对象return new Promise((resolve, reject) => {// 3. 通过XHR发请求const xhr = new XMLHttpRequest()// 不传递方法 使用 GETxhr.open(config.method || 'GET', config.url)xhr.addEventListener('loadend', () => {if (xhr.status >= 200 && xhr.status < 300) {// 绝大多数接口返回的数据格式为 JSONresolve(JSON.parse(xhr.response))} else {reject(new Error(xhr.response))}})xhr.send()})}// 4. 调用函数实现功能hmAxios({// 正确的URLurl: 'http://hmajax.itheima.net/api/province',// 错误的URL 测试用// url: 'http://hmajax.itheima.net/api/province123',method: 'get' // 省略默认为GET}).then(res => {// console.log(res)document.querySelector('.box').innerText = res.list}).catch(err => {console.dir(err)})

什么是回调函数地狱

回调函数地狱示例 
  /*** 需求: 展示数据到下拉框中*  1. 获取省份数据并展示第1个省*  2. 获取第1个省的城市数据,并展示第1个城市*  3. 获取第1个城市的区数据,并展示第1个区* */// 1. 获取省份数据并展示第1个省axios({url: 'http://hmajax.itheima.net/api/province'}).then(res => {// console.log(res)const pname = res.data.list[0]document.querySelector('.province').innerText = pname//  2. 获取第1个省的城市数据,并展示第1个城市axios({url: 'http://hmajax.itheima.net/api/city',params: {pname}}).then(res => {// console.log(res)const cname = res.data.list[0]document.querySelector('.city').innerText = cname// 3. 获取第1个城市的区数据,并展示第1个区axios({// url: 'http://hmajax.itheima.net/api/area',// 故意改错的地址,测试用url: 'http://hmajax.itheima.net/api/area123',params: {pname, cname}}).then(res => {// console.log(res)const aname = res.data.list[0]document.querySelector('.area').innerText = aname}).catch(err => {console.dir(err)})})}).catch(err => {console.dir(err)})

使用 Promises 体现回调函数

当你创建一个 Promise 并使用 .then() 或 .catch() 方法时,你实际上是在注册回调函数,这些回调函数将在 Promise 的状态改变时被调用。

javascript
const myPromise = new Promise((resolve, reject) => {// 异步操作setTimeout(() => {const result = '成功结果';// 根据异步操作的结果,调用 resolve 或 rejectresolve(result);}, 1000);
});// 使用 then() 和 catch() 方法注册回调函数
myPromise.then(result => {// 当 Promise 被解决时,这个回调函数会被调用console.log(result); // 输出: 成功结果}).catch(error => {// 当 Promise 被拒绝时,这个回调函数会被调用console.error(error);});

在这个例子中,resolve 和 reject 函数是 Promise 的执行器(executor function)中的参数,它们分别用来解决(fulfill)或拒绝(reject)Promise。而 .then() 和 .catch() 方法中的参数就是回调函数,它们将在 Promise 状态改变时被调用。

Promise 链式调用

Promise 的另一个强大之处是它们可以进行链式调用。这意味着你可以在一个 Promise 的 .then() 或 .catch() 方法中返回另一个 Promise,这样你就可以创建一系列按顺序执行的异步操作。

function asyncTask1() {return new Promise((resolve, reject) => {setTimeout(() => resolve('任务1完成'), 1000);});
}function asyncTask2() {return new Promise((resolve, reject) => {setTimeout(() => resolve('任务2完成'), 1000);});
}asyncTask1().then(result => {console.log(result); // 输出: 任务1完成return asyncTask2();}).then(result => {console.log(result); // 输出: 任务2完成}).catch(error => {console.error(error);});

在这个例子中,asyncTask1 和 asyncTask2 都是返回 Promise 的函数。通过链式调用,你可以在第一个任务完成后开始第二个任务,而 .catch() 用于捕获整个链中可能出现的任何错误。

总结

Promise 通过 .then() 和 .catch() 方法体现了回调函数的概念,允许你在异步操作成功或失败时执行特定的代码。这种方式比传统的回调函数更清晰、更易于管理,并且支持链式调用,使得异步代码的流程控制更加灵活和强大。

用promise封装的axios解决回调地狱

/*** 需求: 展示数据到下拉框中(Promise链式编程)*  1. 获取省份数据并展示第1个省*  2. 获取第1个省的城市数据,并展示第1个城市*  3. 获取第1个城市的区数据,并展示第1个区* */// 3.1 pname改为全局变量,方便后续使用let pname// 1. 获取省份数据并展示第1个省axios({url: 'http://hmajax.itheima.net/api/province'}).then(res => {// console.log(res)// 3.2 pname保存第一个省的数据pname = res.data.list[0]document.querySelector('.province').innerText = pname// 2. 获取第1个省的城市数据,并展示第1个城市return axios({url: 'http://hmajax.itheima.net/api/city',params: {pname}})}).then(res => {// console.log(res)const cname = res.data.list[0]document.querySelector('.city').innerText = cname// 3.3 获取第1个城市的区数据,并展示第1个区return axios({url: 'http://hmajax.itheima.net/api/area',params: {pname, cname}})}).then(res => {// console.log(res)const aname = res.data.list[0]document.querySelector('.area').innerText = aname})

Promise.all 静态方法

合并多个 Promise 对象,等待所有同时成功完成(或某一个失败),做后续逻辑

代码 
<script>const p = Promise.all([Promise对象, Promise对象, ...])p.then(result => {// result 结果: [Promise对象成功结果, Promise对象成功结果, ...]}).catch(error => {// 第一个失败的 Promise 对象,抛出的异常对象})
</script>

/*** 需求: 查询 北上广深 的天气,并在获取到所有结果之后,渲染到页面上*  城市的code*    北京: 110100*    上海: 310100*    广州: 440100*    深圳: 440300* */// 1. 生成多个Promise对象const p1 = axios({url: 'http://hmajax.itheima.net/api/weather',params: {city: '110100'}})const p2 = axios({url: 'http://hmajax.itheima.net/api/weather',params: {city: '310100'}})const p3 = axios({url: 'http://hmajax.itheima.net/api/weather',params: {city: '440100'}})const p4 = axios({url: 'http://hmajax.itheima.net/api/weather',// 故意改错的,测试用// url: 'http://hmajax.itheima.net/api/weather123',params: {city: '440300'}})// 2. Promise.all 获取所有的成功结果,或者失败原因(第一个)const p = Promise.all([p1, p2, p3, p4])// console.log(p)p.then(res => {// 获取所有Promise对象的成功结果 数组console.log(res)const html = res.map(v => {const { area, weather } = v.data.data// console.log(area, weather)return `<li>${area} -- ${weather}</li>`}).join('')// console.log(html)document.querySelector('.list').innerHTML = html}).catch(err => {// 第一个失败的Promise对象的原因console.dir(err)})

async与awiat

定义

async 是异步的意思,await则可以理解为 async wait。所以可以理解async就是用来声明一个异步方法,而 await是用来等待异步方法执行

async作为一个关键字放在函数前面,表示该函数是一个异步函数,异步函数意味着该函数的执行不会阻塞后面代码的执行;而 await 用于等待一个异步方法执行完成;

await 等待一个 Promise 对象,如果 Promise的状态变成了 resolve 或者 rejcet,那么 async函数会恢复执行。并会阻塞该函数内后面的代码。

使用 async/await 可以实现用同步代码的风格来编写异步代码,这是因为 async/await 的基础技术使用了生成器和 Promise,生成器是协程的实现,利用生成器能实现生成器函数的暂停和恢复。

为了优化 .then 链而开发出来的。

async/await 出现的原因

Promise 的编程模型依然充斥着大量的 then 方法,虽然解决了回调地狱的问题,但是在语义方面依然存在缺陷,代码中充斥着大量的 then 函数,这就是 async/await 出现的原因。async/await 让代码更少,更简洁。

关于async

我们先来看看 async 到底是什么?根据 MDN 定义,async 是一个通过异步执行并隐式返回 Promise 作为结果的函数。对 async 函数的理解,这里需要重点关注两个词:异步执行和隐式返回 Promise。 下面我们会通过例子来看如何 隐式返回 Promise。

async的用法,语法很简单,在函数前面加上async关键字,表示函数是异步的。

 async function timeout() {return 'hello world!'}

那怎么调用呢?async 函数也是函数,平时我们怎么使用函数就怎么使用它,直接加括号调用就可以了,为了表示它没有阻塞它后面代码的执行,我们在async 函数调用之后加一句console.log;

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

打印结果:

发现 timeout() 函数虽然调用了,但是没打印 hello world!; 先不要着急, 看一看timeout() 返回了什么? 把上面的 timeout() 语句改为console.log(timeout())

原来async 函数返回的是一个promise 对象,并且Promise还有state和result,如果async函数中有返回值,当调用该函数时,内部会调用Promise.resolve()方法把它转化成一个promise对象作为返回,但如果timeout函数内部抛出错误呢? 那么就会调用Promise.reject() 返回一个promise 对象

async function timeout() {throw new Error('rejected');
}
console.log(timeout());

就会调用Promise.reject() 返回一个promise 对象

继续修改代码

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

=打印结果:

我们获取到了"hello world!', 同时timeout的执行也没有阻塞后面代码的执行,和 我们刚才说的一致。

 

如果async 函数执行完,返回的promise 没有注册回调函数,比如函数内部做了一次for 循环,你会发现函数的调用,就是执行了函数体,和普通函数没有区别,唯一的区别就是函数执行完会返回一个 promise 对象。

   async function timeout () {for (let index = 0; index < 3; index++) {console.log('async', +index)}}console.log(timeout())console.log('outer')

另外,async函数返回一个promise对象,下面两种方法是等效的

// 方法1
function f() {return Promise.resolve('TEST');
}// asyncF is equivalent to f!// 方法2
async function asyncF() {return 'TEST';
}

关于await
1) await 到底在等啥?

async 关键字差不多了,最重要的就是async函数的执行会返回promise对象,并且把内部的值进行promise的封装。如果promise对象通过then或catch方法又注册了回调函数,async函数执行完以后,注册的回调函数就会放到异步队列中,等待执行。

如果只是async,和promise差不多,但有了await就不一样了,await关键字只能放到async函数里面,await是等待的意思,那么它等待什么呢?它后面跟着什么呢?其实await不仅仅用于等Promise对象,还可以等任意表达式,所以await后面实际是可以接普通函数调用或者直接量的,不过我们更多的是放一个返回promise 对象的表达式。他等待的是promise对象执行完毕,并返回结果。

//所以下面这个示例完全可以正确运行function getSomething () {return 'something'}async function testAsync () {return Promise.resolve('hello async')}async function test () {const v1 = await getSomething()const v2 = await testAsync()console.log(v1, v2)}test()

2) await 等到了要等的,然后呢?

await 等到了它要等的东西,一个 Promise 对象,或者其它值,然后呢?

如果它等到的是一个Promise对象,await就忙起来了,它会阻塞函数后面的代码,等着Promise对象resolve/reject,然后得到resolve/reject的值,作为await表达式的运算结果。

如果 await 等待的是一个非 Promise 对象,那么V8 会隐式地将该对象包装成一个已经 resolve 的 Promise 对象.

async function example() {console.log('开始执行 async 函数');// 等待一个非 Promise 对象const value = await 123; // 123 被隐式转换为一个已解决的 Promiseconsole.log('非 Promise 对象的值:', value);// 继续执行,没有暂停const result = await new Promise(resolve => setTimeout(() => resolve('Promise 结果'), 1000));console.log('Promise 的结果:', result);
}example();

await 优势在于处理 then 链,使代码看起来像同步代码一样

现在写一个函数,让它返回promise 对象,该函数的作用是2s 之后让数值乘以2

// 2s 之后返回双倍的值
function doubleAfter2seconds (num) {return new Promise((resolve, reject) => {setTimeout(() => {resolve(num * 2)}, 2000)})}

现在再写一个async 函数,从而可以使用await 关键字, await 后面放置的就是返回promise对象的一个表达式,所以它后面可以写上 doubleAfter2seconds 函数的调用

async function testResult() {let result = await doubleAfter2seconds(30);console.log(result); //2s后打印60
}
testResult();

代码的执行过程

调用testResult 函数,它里面遇到了await, await 表示等待,代码就暂停到这里,不再向下执行了,它等待后面的promise对象执行完毕,然后拿到promise resolve 的值并进行返回,返回值拿到之后,它继续向下执行。具体到 我们的代码, 遇到await 之后,代码就暂停执行了, 等待doubleAfter2seconds(30) 执行完毕,doubleAfter2seconds(30) 返回的promise 开始执行,2秒 之后,promise resolve 了, 并返回了值为60, 这时await 才拿到返回值60, 然后赋值给result, 暂停结束,代码继续执行,执行 console.log语句。

就这一个函数,我们可能看不出async/await 的作用,如果我们要计算3个数的值,然后把得到的值进行输出呢?

async function testResult() {let first = await doubleAfter2seconds(30);let second = await doubleAfter2seconds(50);let third = await doubleAfter2seconds(30);console.log(first + second + third);
}
testResult()

6秒后,控制台输出220, 我们可以看到,写异步代码就像写同步代码一样了,再也没有回调地域了。

这里强调一下,当js引擎在等待promise.resolve的时候,他并没有真正的暂停工作,它可以处理其他的一些事情,如果我们在testResult函数后面继续执行其他代码,比如console.log一下,会发现console.log代码先执行。

async function testResult() {let first = await doubleAfter2seconds(30);let second = await doubleAfter2seconds(50);let third = await doubleAfter2seconds(30);console.log(first + second + third);
}
testResult()
console.log('我先执行!!!')
1

先输出 “我先执行!!!”,6s后输出计算结果。

综合应用

/*** 需求: 获取所有分类数据并同时渲染到页面上*  1. 获取一级商品分类*  2. 获取所有二级商品分类*  3. 渲染数据* */async function func() {// 1. 获取一级商品分类const res1 = await axios({url: 'http://hmajax.itheima.net/api/category/top'})console.log(res1)// 2. 获取所有二级商品分类// 基于数据[]-map-》Promise数组[]const pArray = res1.data.data.map(v => {const { id } = vreturn axios({url: 'http://hmajax.itheima.net/api/category/sub',params: {id}})})console.log(pArray)// Promise.all 等待所有二级分类接收获取到数据const result = await Promise.all(pArray)console.log(result)// 3. 渲染数据const html = result.map(v => {const { name, children } = v.data.datareturn `<div class="item"><h3>${name}</h3><ul>${children.map(c => {return `<li><a href="javascript:;"><img src="${c.picture}"><p>${c.name}</p></a></li>`}).join('')}            </ul></div>`}).join('')document.querySelector('.sub-list').innerHTML = html
}func()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/392623.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Idea绿色下载安装教程-最新,2024版本通用-附下载链接

插件链接&#xff1a; 脚本 Idea下载安装完成后 进入激活码输入页面&#xff0c;然后关闭IDEA 按照下面流程进行激活 1. 按照以下步骤&#xff0c;亲测可用&#xff0c;记得一定要先关闭idea 2. 选择对应软件 3.选择bin、目录对应选项 5.激活 6.成功

ROS2 Humble 学习【openEuler】

ROS2 Humble 学习 1 介绍1.1 概述1.2 ROS2 详细介绍1.3 openEuler 安装 ROS2 Humble1.4 ROS2 系统架构 2 ROS2 基础2.1 节点编写、编译、运行【简单示例】节点编写节点编译 g节点运行节点编译 make节点编译 CMakeLists.txtCMake依赖查找流程Python 依赖查找流程 2.2 节点交互、…

LeetCode | 441 | 排列硬币 | 二分查找

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 今天分享的是LeetCode中一道标签为简单的算法题&#xff0c;本质是一道数学题 文章目录 1.题目描述2.题解2.1 公式解法2.2 暴力解法2.3 二分查找 LeetCode链接&#…

【51单片机仿真】基于51单片机设计的钟表定时闹钟系统仿真源码设计文档演示视频——完整资料下载

演示视频 设计内容 &#xff08;1&#xff09;使用 DS1302 结合字符型 LCD12864 显示器设计一个简易的定时闹钟 LCD 时钟。程序执行后 LCD 显示“00&#xff1a;00&#xff1a;00” &#xff08;2&#xff09;K1—设置现在的时间&#xff0c;年闪烁&#xff0c;再按 K1 键月闪…

15.75.【C语言】表达式求值

目录 一.整型提升 1.定义 2. 一.整型提升 1.定义 C语言中整型算术运算总是至少以缺省&#xff08;默认&#xff09;整型类型的精度来进行的。为了获得这个精度&#xff0c;表达式中的字符和短整型操作数在使用之前被转换为普通整型&#xff0c;这种转换称为整型提升 2.整型提…

njs、nginx JavaScript、在nginx上写JavaScript、nginx支持js

njs、nginx JavaScript、在nginx上写JavaScript、nginx支持js 现在是 2024-08-05 &#xff0c;在一个月前&#xff0c;我逛nginx官网&#xff0c;还没有这个模块的介绍。看njs官网&#xff0c;在四年前已经创建这个项目。不知道是不是近期才把这个项目纳入。以前不知道这模块&…

C# 构建观测者模式(或者为订阅者模型)

前言&#xff1a; 观测者模型的基本理念&#xff0c;就是&#xff0c;我有一个公共的事件&#xff0c;定义好他的事件的触发、数据接口。然后&#xff0c;通过增加订阅者&#xff08;实例&#xff09;来订阅这个事件的&#xff0c;或者说观察这个事件。如果事件发生&#xff0…

未授权访问漏洞系列详解⑥!

JBoss未授权访问漏洞 JBoss是一个基于J2EE的开放源代码应用服务器&#xff0c;代码遵循LGPL许可&#xff0c;可以在任何商业应用中免费使用;JBoss也是一个管理EJB的容器和服务器&#xff0c;支持EJB1.1、EJB 2.0和EJB3规范。,默认情况下访问 http://ip:8080/jmx-console 就可以…

【Java数据结构】---初始数据结构

乐观学习&#xff0c;乐观生活&#xff0c;才能不断前进啊&#xff01;&#xff01;&#xff01; 我的主页&#xff1a;optimistic_chen 我的专栏&#xff1a;c语言 &#xff0c;Java 欢迎大家访问~ 创作不易&#xff0c;大佬们点赞鼓励下吧~ 前言 从今天开始我们就要学习Java…

Altium designer学习笔记03 -原理图绘制

原理图绘制 1. 原理图页大小设置2.原理图格点的设置3. 原理图模板的应用4. 元件的放置5.元件属性的编辑6.元件的选择、移动、旋转、镜像6.1 元件的选择6.2 元件的移动6.3 元件的旋转6.3 元件的镜像 7.元件的复制/剪切/粘贴8.元件的排列与对齐9.绘制导线的导线属性设置10.放置网…

实时数仓分层架构详解

首先&#xff0c;我们从数据仓库说起。 数据仓库的概念可以追溯到20世纪80年代&#xff0c;当时IBM的研究人员提出了商业数据仓库的概念。数据仓库概念的提出&#xff0c;是为了解决和数据流相关的各种问题&#xff0c;特别是多重数据复制带来的高成本问题。 数据仓库之父Bill …

简单反射型XSS的复现

xss反射型攻击&#xff1a; 1.最简单的漏洞复现&#xff1a; 这里我们有一个最简单的网页&#xff1a;由于地址不存在&#xff0c;所以图片加载不出来。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta…

skynet 连接redis

文章目录 概述main.luaagent.luaredis.lua 小结 概述 之前写过skynet 入门篇&#xff0c;还有skynet实操篇&#xff1b;这2篇&#xff0c;主要写了skynet如何使用&#xff0c;还有些skynet的调用流程之类。 其实&#xff0c;看过skynet的demo之后&#xff0c;发现skynet中没有…

Simulink模型开发中的一些自动化方法

随着Simulink模型的产品化开发进程&#xff0c;许多模型开发人员会关心模型的建模自动化问题。比如如何对模型中的元素进行批量查找和修改&#xff1b;如何构建自己的建模规则对模型进行检查&#xff1b;如何实现测试自动化等。在这些使用场景中我们都需要了解一些Simulink函数…

谈谈冯诺依曼体系

我们都知道冯诺依曼体系这张图最为代表性&#xff0c;而接下来我们就来浅谈一下各部分之间的作用~ 输入设备&#xff1a;键盘&#xff0c;磁盘&#xff0c;网卡&#xff0c;话筒等等 输出设备&#xff1a;磁盘&#xff0c;网卡&#xff0c;声卡&#xff0c;显示屏等等 这些硬件…

1.1、centos stream 9安装Kubernetes v1.30集群 环境说明

最近正在学习kubernetes&#xff0c;买了一套《Kubernetes权威指南 从Docker到Kubernetes实践全接触(第六版)》这本书讲得很好&#xff0c;上下两册&#xff0c;书中k8s的版本是V1.29&#xff0c;目前官网最新版本是v1.30。强烈建议大家买一套看看。 Kubernetes官网地址&#x…

sql注入——二次注入

二次注入 简介工具环境具体实施 简介 二次注入是一种较为隐蔽的 SQL 注入攻击方式。它并非直接在输入时进行攻击&#xff0c;而是先将恶意数据存储到数据库中&#xff0c;这些数据看似正常。随后&#xff0c;应用程序在后续的操作中&#xff0c;再次使用或处理这些之前存储的恶…

消息队列:Kafka吞吐量为什么比RocketMQ大

根据资料显示RocketMQ每秒能处理10W量级数据&#xff0c;而Kafka能处理17W量级数据。 这两者差别主要再使用的零拷贝技术不一样。 再什么情况下零拷贝技术诞生了 为了防止消息队列中的消息因为各种意外情况丢失&#xff0c;要对消息进行持久化处理&#xff0c;将其存储在磁盘…

NLP——文本预处理

本文思维导图 文本预处理及其作用 文本语料在输送给模型前一般需要一系列的预处理工作, 才能符合模型输入的要求, 如: 将文本转化成模型需要的张量, 规范张量的尺寸等, 而且科学的文本预处理环节还将有效指导模型超参数的选择, 提升模型的评估指标. 一、文本处理的基本方法 1…

C++ | Leetcode C++题解之第326题3的幂

题目&#xff1a; 题解&#xff1a; class Solution { public:bool isPowerOfThree(int n) {return n > 0 && 1162261467 % n 0;} };