异步(二)回调地狱
一.回调地狱
在谈到回调地狱发生的情况和解决办法,需要先了解ajax请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 先列出服务器提供的数据接口: app. get ( '/data1' , (req, res) => { res.send( 'hi' ) }) app. get ( '/data2' , (req, res) => { res.send( 'hello' ) }) app. get ( '/data3' , (req, res) => { res.send( 'nihao' ) }) // 启动监听 app.listen(3000, () => { console.log( 'running...' ) }) |
原生ajax请求步骤
1 2 3 4 5 6 7 8 9 10 | let xhr = new XMLHttpRequest(); xhr.open( 'get' , 'url' ) xhr.send( null ); xhr.onreadystatechange = function(){ if (xhr.readyState ===4 && xhr.status ===200) { let ret = xhr.responseText; console.log(ret) } } |
函数封装
上面有data1,data2,data3,三次请求,那么就需要写三个ajax,这时就会想到用函数来封装
function queryData(path,callback){ let xhr = new XMLHttpRequest(); xhr.open('get','url'+path) xhr.send(null); xhr.onreadystatechange = function(){ if(xhr.readyState===4 && xhr.status ===200) { let ret = xhr.responseText; callback(ret) } } } //调用 queryData('data1',function(ret){ console.log(ret) })
如何形成的回调地狱
如果想按顺序获取接口'data1','data2','data3' 中的数据,就会进行下面的操作
queryData('data1',function(ret){ console.log(ret) //按顺序第一个输出:hi queryData('data2',function(ret){ console.log(ret) //第二个输出 hello queryData('data3',function(ret){ console.log(ret) //第三个输出:你好 }) }) }) 如果这里有100个,1000个,甚至更多的请求呢,那真是一场灾难
promise方式
为了改造上面的回调函数问题,诞生了promise.promise其实就是一种语法糖(代码形式发生改变,但是功能不变)
function queryData(path) { return new Promise(function(resolve, reject) { // 需要在这里处理异步任务 var xhr = new XMLHttpRequest(); xhr.open('get','http://localhost:3000/' + path); xhr.send(null); xhr.onreadystatechange = function() { // 该函数何时触发?xhr.readyState状态发生变化时 if(xhr.readyState != 4) return; if(xhr.readyState == 4 && xhr.status == 200) { // 获取后台数据 var ret = xhr.responseText; // 成功的情况 resolve(ret); } else { // 失败的情况 reject('服务器错误'); } } }) }
分析
queryData('data1') .then(ret=>{ console.log(ret) // 顺序输出第一个结果为:hi // 如果在then方法中没有返回Promise实例对象,那么下一个then由默认产生的Promise实例对象调用 }) .then(ret=>{ console.log('-------------------' + ret) // 顺序输出第二个结果为:----------------------undefined // 如果在then中显式地返回一个具体数据,那么下一个then可以获取该数据 return 456; }) .then(ret=>{ console.log('-------------------' + ret) // 顺序输出第三个结果为:----------------------456 })
promist对象除了.then方法外,还有两个方法可以通过,调用,其中finally是ES&中新增的方法
.catch(ret=>{ // 发生错误时触发 console.log('error') }) .finally(ret=>{ // 无论结果成功还是失败都触发:一般用于释放一些资源 console.log('finally') })
async和await 进行解决
function queryData(path) { return new Promise(function(resolve, reject) { // 需要在这里处理异步任务 var xhr = new XMLHttpRequest(); xhr.open('get','http://localhost:3000/' + path); xhr.send(null); xhr.onreadystatechange = function() { // 当readyState值不为0的时候直接返回 if(xhr.readyState != 4) return; if(xhr.readyState == 4 && xhr.status == 200) { // 获取后台数据 var ret = xhr.responseText; // 成功的情况 resolve(ret); } else { // 失败的情况 reject('服务器错误'); } } }) } async function getAllData() { // await执行流程是顺序执行 let ret1 = await queryData('data1'); let ret2 = await queryData('data2'); let ret3 = await queryData('data3'); console.log(ret1) console.log(ret2) console.log(ret3) } getAllData();
需要注意一点:async函数的返回值是Promise实例对象
async function getAllData() { // await执行流程是顺序执行 let ret1 = await queryData('data1'); return 'hello'; } var ret = getAllData(); console.log(ret) // 这里输出一个promise对象,并且resolve的数据为hello ret.then(res=>{ console.log(res) // 这里输出结果为:hello })
以上内容来自:https://www.cnblogs.com/belongs-to-qinghua/p/11161140.html
结合promise,做一个小案例,模拟一个对话页面,输入文字,通过接口,获取机器人的对话,同时将对话转为语音放出来
<script> const baseUrl = "http://www.liulongbin.top:3006"; window.onload = function () { document .querySelector("#btnSend") .addEventListener("click", function () { let str = document.querySelector("#ipt").value.trim(); if (!str) { alert("信息不能为空"); return; } appendContent(str, "right"); document.querySelector("#ipt").value = ""; //获取机器人信息 getJiQiRXX(str) .then((res) => { appendContent(res.data.info.text); return getJiQiYY(res.data.info.text); }) .then((res) => { // console.log("语音内容", res); document.querySelector("audio").src = res.voiceUrl; }); }); }; function getJiQiRXX(spoken) { return new Promise((resolve, reject) => { //请求机器人信息 let xhr = new XMLHttpRequest(); xhr.open("get", baseUrl + "/api/robot?spoken=" + spoken); xhr.send(null); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { let data = JSON.parse(xhr.responseText); resolve(data); // console.log(xhr.responseText); } else { reject("请求失败,请稍后再试"); } } }; }); } function getJiQiYY(text) { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.open("get", baseUrl + "/api/synthesize?text=" + text); xhr.send(null); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { let data = JSON.parse(xhr.responseText); resolve(data); } else { reject("请求失败,请稍后再试"); } } }; }); } // function scrollChange() {} function appendContent(str, who) { let li = document.createElement("li"); li.className = who === "right" ? "right_word" : "left_word"; li.innerHTML = `<img src="img/${ who === "right" ? "person02.png" : "person01.png" }" /> <span>${str}</span>`; document.querySelector("#talk_list").appendChild(li); //滚动 document.querySelector("#talk_list").scrollTop = document.querySelector( "#talk_list" ).scrollHeight; } </script>
结合async await,做一个小案例,模拟一个对话页面,输入文字,通过接口,获取机器人的对话,同时将对话转为语音放出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | const baseUrl = "http://www.liulongbin.top:3006" ; window.onload = function () { document .querySelector( "#btnSend" ) .addEventListener( "click" , function () { let str = document.querySelector( "#ipt" ).value.trim(); if (!str) { alert( "信息不能为空" ); return ; } appendContent(str, "right" ); document.querySelector( "#ipt" ).value = "" ; // //获取机器人信息 // getJiQiRXX(str) // .then((res) => { // appendContent(res.data.info.text); // //获取语音 // return getJiQiYY(res.data.info.text); // }) // .then((res) => { // // console.log("语音内容", res); // document.querySelector("audio").src = res.voiceUrl; // }); getAllData(str); }); }; async function getAllData(spoken) { //获取机器人信息 let res1 = await getJiQiRXX(spoken); appendContent(res1.data.info.text); //获取语音信息 let res2 = await getJiQiYY(res1.data.info.text); document.querySelector( "audio" ).src = res2.voiceUrl; } function getJiQiRXX(spoken) { return new Promise((resolve, reject) => { //请求机器人信息 let xhr = new XMLHttpRequest(); xhr.open( "get" , baseUrl + "/api/robot?spoken=" + spoken); xhr.send( null ); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { let data = JSON.parse(xhr.responseText); resolve(data); // console.log(xhr.responseText); } else { reject( "请求失败,请稍后再试" ); } } }; }); } function getJiQiYY(text) { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.open( "get" , baseUrl + "/api/synthesize?text=" + text); xhr.send( null ); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { let data = JSON.parse(xhr.responseText); resolve(data); } else { reject( "请求失败,请稍后再试" ); } } }; }); } // function scrollChange() {} function appendContent(str, who) { let li = document.createElement( "li" ); li.className = who === "right" ? "right_word" : "left_word" ; li.innerHTML = `<img src="img/${ who === "right" ? "person02.png" : "person01.png" }" /> <span>${str}</span>`; document.querySelector( "#talk_list" ).appendChild(li); //滚动 document.querySelector( "#talk_list" ).scrollTop = document.querySelector( "#talk_list" ).scrollHeight; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现