- A+
所属分类:Web前端
一.回调地狱
在谈到回调地狱发生的情况和解决办法,需要先了解ajax请求
先列出服务器提供的数据接口: 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请求步骤
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,做一个小案例,模拟一个对话页面,输入文字,通过接口,获取机器人的对话,同时将对话转为语音放出来
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" alt="异步(二)回调地狱" : "person01.png" }" /> <span>${str}</span>`; document.querySelector("#talk_list").appendChild(li); //滚动 document.querySelector("#talk_list").scrollTop = document.querySelector( "#talk_list" ).scrollHeight; }