一.回调地狱
在谈到回调地狱发生的情况和解决办法,需要先了解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" : "person01.png"
}" /> <span>${str}</span>`;
document.querySelector("#talk_list").appendChild(li);
//滚动
document.querySelector("#talk_list").scrollTop = document.querySelector(
"#talk_list"
).scrollHeight;
}