由于js没有多线程,所以处理多任务的时候,可以用异步回调来解决。js中setTimeout、setInterval、ajax(jq中可以选择同步或异步)均会开启异步。遇到异步模块,会将其推入值任务队列中,继续向下执行
最后等待异步模块处理完成后,cpu会自动接收到通知,然后从任务队列中取出执行。
先来看个需求
我有一个页面,和两个接口,一个是获取所有老师,一个是根据老师id获得该老师所管理的学生。要求是在该页面都展示出来。
后端代码
// 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示: const Koa = require('koa2'); const Router = require('koa-router'); // 创建一个Koa对象表示web app本身: const app = new Koa(); const router=new Router(); const bodyParser = require('koa-bodyparser')//解析请求参数中间件 app.use(bodyParser()) // cors跨域 app.use(async (ctx, next) => { console.log(`Process ${ctx.request.method} ${ctx.request.url}...`); ctx.set("Access-Control-Allow-Origin", "*"); ctx.set("Access-Control-Allow-Headers", "x-requested-with, accept, origin, content-type"); ctx.set("Content-Type", "application/json,application/x-www-form-urlencoded,text/plain"); await next(); }); //获取所有老师 router.post('/getTeacher', async ( ctx ) => { let st=[ {id:20170101,name:'张老师',age:48}, {id:20170102,name:'王老师',age:36}, {id:20170103,name:'丁老师',age:52} ]; ctx.response.type = 'application/json'; ctx.body ={ total:st.length, rows:st } }) //获取老师名下的学生 router.post('/getTs', async ( ctx ) => { let reqParam= ctx.request.body; let tid=reqParam.tid;//teacher的id,是个数组 let data={ 20170101:{ tname:'张老师', students: [ {id:20170201,name:'小明',age:12}, {id:20170202,name:'小红',age:10} ] }, 20170102:{ tname:'王老师', students: [ {id:20170203,name:'小白',age:11} ] }, 20170103:{ tname:'丁老师', students: [ {id:20170204,name:'小青',age:8}, {id:20170205,name:'小紫',age:10} ] } }; let matching={}; let unMatching=[]; tid.forEach(function(item){ if(data[item]==undefined){ unMatching.push(item) }else{ matching[item]=data[item] } }) let rep={ unMatching:unMatching,//匹配到的 matching:matching//未匹配到的 } ctx.response.type = 'application/json'; ctx.body =rep; }) // 加载路由中间件 //解释:app.use 加载用于处理http請求的middleware(中间件),当一个请求来的时候,会依次被这些 middlewares处理。 app.use(router.routes()); // 在端口3000监听: app.listen(1234, () => { console.log('[myapp]已经运行,端口为1234') })
{ "name": "test", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "koa2": "^2.0.0-alpha.7", "koa-router": "^7.3.0", "koa-bodyparser": "^4.2.0" } }
接口返回数据如下
请求方式:post,参数:无
请求方式:post,参数:tid:[20170101,20170102,20170102]
效果图如下
准备工作
先把页面基础布局写好,因为我不想拼接字符串,所以用了腾讯的字符串模板引擎artTemplate
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> table{border-collapse:collapse ;} td,th{border: red solid 1px; width: 80px; text-align: left;} </style> </head> <body> <script id="tsTp" type="text/html"> <h1>老师所照顾的学生‘对应表’</h1> {{each ts v k}} <fieldset> <legend>{{v.tname}}</legend> <table > <thead> <tr> <th>姓名</th> <th>年龄</th> </tr> </thead> <tbody> {{each v.students st}} <tr> <td>{{st.name}}</td> <td>{{st.age}}</td> </tr> {{/each}} </tbody> </table> </fieldset> {{/each}} </script> <button>测试</button> <script src="lib/jquery-3.2.1.min.js"></script> <script src="lib/template-web.js"></script> <script src="js/index.js"></script> </body> </html>
Es5解决(嵌套式回调)
var service={ //获取老师 getTeacher:function(calBack){ $.post('http://47.93.52.112:1234/getTeacher',function(rep){ calBack(rep) }) }, //获取老师对应下的学生(Ts:某个老师的学生) getTs:function(param,calBack){ $.post('http://47.93.52.112:1234/getTs',param,function(rep){ calBack(rep) }) } } $('button').click(function(){ service.getTeacher(function(rep){ let teachers=rep.rows; let tid=[]; teachers.forEach(function(item){tid.push(item.id)}) service.getTs({tid:tid},function(rep){ let ts=rep.matching; //渲染至页面 let tsTpData={ts:ts}; $('body').html(template('tsTp', tsTpData)); }) }) })
Es6解决(promise,链式回调)
这样可以避免回调函数的地狱
var service={ //获取老师 getTeacher:function(){ return new Promise(function(resolve, rejec){ $.post('http://47.93.52.112:1234/getTeacher',function(rep){ resolve(rep) }) }) }, //获取老师对应下的学生(Ts:某个老师的学生) getTs:function(param){ return new Promise(function(resolve, rejec){ $.post('http://47.93.52.112:1234/getTs',param,function(rep){ resolve(rep) }) }) } } $('button').click(function(){ service.getTeacher().then(function(rep){ let teachers=rep.rows; let tid=[]; teachers.forEach(function(item){tid.push(item.id)}) service.getTs({tid:tid}).then(function(rep){ let ts=rep.matching; //渲染至页面 let tsTpData={ts:ts}; $('body').html(template('tsTp', tsTpData)); }) }) })
注释:
新版的jq的ajax,本身已经实现了promise的链式回调,故我们可以直接使用,我这里只是演示
Es7解决(异步的终极解决方案-Async/Await)
目标就像同步调用那么爽,感觉不出来是回调
- 可以让异步逻辑用同步写法实现
- 最底层的await返回需要是Promise对象
- 可以通过多层 async function 的同步写法代替传统的callback嵌套
var service={ //获取老师 getTeacher:async function(){ let result=await $.post('http://47.93.52.112:1234/getTeacher'); return result; }, //获取老师对应下的学生(Ts:某个老师的学生) getTs:async function(param){ let result=await $.post('http://47.93.52.112:1234/getTs',param); return result; } } $('button').click(async function(){ let teachers=(await service.getTeacher()).rows; let tid=[]; teachers.forEach(function(item){tid.push(item.id)}); let ts=(await service.getTs({tid:tid})).matching; //渲染至页面 let tsTpData={ts:ts}; $('body').html(template('tsTp', tsTpData)); })
一个完整的前后端demo
前端:jq、后端:node
前端
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> table{border-collapse: collapse;} td,th{ border: red solid 1px; padding:4px 20px ; } </style> </head> <body> <table> <thead> <tr> <th>姓名</th> <th>性别</th> <th>邮寄地址</th> </tr> </thead> <tbody class="tbd"></tbody> </table> <div class="ybs"></div> <script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script> <script> //service let service={ getStudents() { return $.get('http://www.dshvv.com:3000/getStudents'); }, getStuDetail(param){ return $.post('http://www.dshvv.com:3000/getStuDetail',param); }, getYb(param){ return $.post('http://www.dshvv.com:3000/getYb',param); } } //初始化执行 let inits = async ()=>{ //获取所有的学生列表 let students=await service.getStudents(); let tbodyDom=''; students.forEach(function (item) { tbodyDom+=`<tr> <td>${item.name}</td> <td>${item.sex}</td> <td class="ybTouch" data-sid="${item._id}">点击查看邮编</td> </tr>`; }) $('.tbd').html(tbodyDom) } inits() //事件 $(document).on('click', '.ybTouch', async function() { //获取学生详情 let stuDetail=await service.getStuDetail({sid:$(this).data('sid')}); let ybResult=await service.getYb({adds:stuDetail.province}); $('.ybs').html(`查询到此人家的邮编是:${ybResult.result}`) }); </script> </body> </html>
后端
module.exports=function (route) { route .get('/getStudents',async ctx=>{ ctx.response.type = 'text/json'; ctx.response.body=[{ "_id" : "5b4da81c3b28c3253a3c168a", "sex" : "女", "name" : "王丽云" },{ "_id" : "5b4da81c3b28c3253a3c168b", "sex" : "男", "name" : "丁少" }, { "_id" : "5b4da81c3b28c3253a3c168c", "sex" : "男", "name" : "张中秋" }] }) .post('/getStuDetail',async ctx=>{ ctx.response.type = 'text/json'; let param=ctx.request.body; console.log(param) let sid=param.sid; let sdt={ "5b4da81c3b28c3253a3c168a":{ "_id" : "5b4da81c3b28c3253a3c168a", "province" : "郑州市", "sex" : "女", "name" : "王丽云", "birth" :"20180101" }, "5b4da81c3b28c3253a3c168b":{ "province" : "广州市", "sex" : "男", "name" : "丁少", "birth" :"20180102" }, "5b4da81c3b28c3253a3c168c":{ "province" : "北京市", "sex" : "男", "name" : "张中秋", "birth" :"20180102" } } ctx.response.body=sdt[sid] }) .post('/getYb',async ctx=>{ ctx.response.type = 'text/json'; let param=ctx.request.body; let adds=param.adds; let ybs={ "郑州市":450000, "广州市":510000, "北京市":100000 } let res=''; for(let itm in ybs){ if(itm==adds){ res=ybs[itm] } } ctx.response.body={ result:res } }) }
效果
参考:
在 Node.js 的开发中,由于逻辑分层所致,会出现多层回调。易于引发异常处理混乱、闭包过于复杂、代码难以维护等问题。于是就引发了异步编程优雅解决的各大方案
http://cnodejs.org/topic/5640b80d3a6aa72c5e0030b6
http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html