异步(二)回调地狱

  • A+
所属分类:Web前端
摘要

一.回调地狱在谈到回调地狱发生的情况和解决办法,需要先了解ajax请求 原生ajax请求步骤

一.回调地狱

在谈到回调地狱发生的情况和解决办法,需要先了解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;       }