js的同步异步

由于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')
})
View Code
{
  "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"
  }
}
View Code

接口返回数据如下
请求方式: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));
        })
    })
})
View Code

 

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));
        })
    })
})
View Code

注释:
新版的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));
})
View Code

一个完整的前后端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

原文地址:https://www.cnblogs.com/dshvv/p/7691227.html