js多个异步请求,按顺序执行next
2017-10-27 14:28 muamaker 阅读(15099) 评论(2) 编辑 收藏 举报在js里面,偶尔会遇见需要多个异步按照顺序执行请求,又不想多层嵌套,,这里和promise.all的区别在于,promise或者Jquery里面的$.when 是同时发送多个请求,一起返回,发出去的顺序是一起;这里是按照顺序发请求
方法 一 、首先创建一个迭代器,接收任意多个函数参数
function nextRegister(){ var args = arguments; var count = 0; var comm = {}; function nextTime(){ count++; if(count < args.length){ if(args[count] && Object.prototype.toString.call(args[count]) == '[object Function]'){ args[count](comm,nextTime); } } } if(args[count] && Object.prototype.toString.call(args[count]) == '[object Function]'){ args[count](comm,nextTime); } }
创建多个异步的函数,注入到迭代器中
/* comm:多个函数,公用的变量 next:调用下一个函数 * */ function fn1(comm,next){ console.log('1'); comm.age = 20; next(); } function fn2(comm,next){ next(); console.log('2'); console.log(comm.age); } function fn3(comm,next){ console.log('3'); } //开始执行迭代 nextRegister(fn1,fn2,fn3);
在这里,fn1-fn3函数中,做异步操作,知道在异步成功的时候调用next()就可以继续执行下一个函数,同时可以将前面函数返回的结果,绑定在comm上,带到下一个函数中
方法 二、参考express 和 koa 的写法
1、es5写法
function Iterator(){ this.middlewares = []; } Iterator.prototype.use = function(fn){ this.middlewares.push(fn); return this; } Iterator.prototype.run = function(ctx){ function createNext(middleware, oldNext) { return function(){ middleware(ctx, oldNext) }; } let len = this.middlewares.length; let next = function(){}; for (let i = len - 1; i >= 0; i--) { let currentMiddleware = this.middlewares[i]; next = createNext(currentMiddleware, next); //从后往前遍历,前面的闭包保存后面的 next } next(); } var iterator = new Iterator(); iterator.use(function(ctx,next){ //这里可以做一些异步操作,只需要成功后调用 next console.log("start:a"); next(); console.log("end:a"); }); iterator.use(function(ctx,next){ console.log("start:b"); next(); console.log("end:b"); }); iterator.run();
2、es6 的 async 写法
class Iterator{ constructor(){ this.middlewares = []; } use(fn){ this.middlewares.push(fn); //存入任务 return this; } async run(ctx){ function createNext(middleware, oldNext) { return async () => { await middleware(ctx, oldNext); } } let len = this.middlewares.length; let next = async () => { return Promise.resolve(); }; for (let i = len - 1; i >= 0; i--) { let currentMiddleware = this.middlewares[i]; next = createNext(currentMiddleware, next); } await next(); } } let app = new Iterator(); app.use(async (ctx,next)=>{ console.log("start:a"); await next(); console.log("end:a"); }); app.use(async (ctx,next)=>{ console.log("start:b"); await next(); console.log("end:b"); }); app.run();
三 、扩展 基于上面的 二 的迭代器,利用 node 的 原生 http 模块,可以简单的创建一个服务框架。
这里写一个简单的静态文件查看服务
const http = require('http'); const fs = require("fs"); const promisify = require("util").promisify; const readFile = promisify(fs.readFile); const readdir = promisify(fs.readdir); const path = require("path"); //创建web服务器,提供服务,处理客户端的请求 //普通方式监听 class MyServer{ constructor() { const me = this; me.middlewares = []; me.methods = {}; me.server = http.createServer(async (req,res)=>{ //req客户端请求的相关信息,res返回响应信息 //let url = req.url; let ctx = { res, req, stateCode:200, headers:{'Content-Type': 'text/plain;charset=utf-8'}, send(str,headers){ let res = ctx.res; res.writeHead(ctx.stateCode || 200, headers || ctx.headers); res.end(str); } }; await me.run(ctx); //开始执行任务 }); me.server.on("error",function(e){ me.trigger("error",e); }); me.server.on("listening",function(e){ var addr = me.server.address(); var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port; console.log('Listening on ' + bind); }) } listen(port){ this.server.listen(port || 9000); return this; } use(fn){ this.middlewares.push(fn); //存入任务 return this; } async run(ctx){ function createNext(middleware, oldNext) { return async () => { await middleware(ctx, oldNext); } } let len = this.middlewares.length; let next = async () => { return Promise.resolve(); }; for (let i = len - 1; i >= 0; i--) { let currentMiddleware = this.middlewares[i]; next = createNext(currentMiddleware, next); } try{ await next(); }catch(e){ //解决中文乱码 this.trigger("error",e); } } on(type,fn){ if(!this.methods[type]){ this.methods[type] = []; } this.methods[type].push(fn); } trigger(type,arg){ if(this.methods[type]){ for(var i = 0; i < this.methods[type].length; i++){ this.methods[type][i](arg); } }else if(type === "error"){ throw new Error(arg); } } } var server = new MyServer(); //错误处理 server.use(async (ctx,next)=>{ try{ await next(); }catch(e){ ctx.stateCode = 500; ctx.send('服务器错误:' + e.message); } }); //开启跨域 server.use(async ({res},next)=>{ res.setHeader("Access-Control-Allow-Origin", "*"); // 设置可访问的源 res.setHeader("Access-Control-Allow-Headers", "Content-Type"); next(); }); //其他逻辑正常处理 const base = __dirname; //默认是当前文件为静态资源目录, 可以为 "F:\\work" //尝试读取文件 server.use(async (ctx,next)=>{ //初始化路径 if(ctx.req.url === "/"){ ctx.filePath = base || __dirname; }else{ ctx.filePath = path.join(base || __dirname,decodeURIComponent(ctx.req.url)); } try{ let res = await readFile(ctx.filePath); ctx.send(res); }catch(e){ next(); } }); //尝试读取 index.html server.use(async (ctx,next)=>{ try{ if(ctx.filePath.indexOf(".") === -1){ let res = await readFile(ctx.filePath+"/index.html","utf-8"); ctx.headers["Content-Type"] = "text/html;charset=utf-8"; ctx.send(res); }else{ next(); } }catch(e){ next(); } }); //尝试读取文件夹, 并展示 server.use(async (ctx,next)=>{ try{ let res = await readdir(ctx.filePath); var str = []; var prex = ctx.req.url; if(ctx.req.url !== "/"){ var parent = path.dirname(ctx.req.url); str = [ `<h3><a href="${parent}">.../返回上级</a> 共 ${res.length} 个文件</h3>` ]; }else{ prex=""; } for(let i = 0; i < res.length; i++){ str.push(`<p><a href="${prex}/${res[i]}">${res[i]}</a></p>`) } var html = `<div style="text-align:center;">${str.join("")}</div>`; ctx.headers["Content-Type"] = "text/html;charset=utf-8"; ctx.send(html); }catch(e){ console.log(e) next(); } }); //404处理 server.use(async (ctx,next)=>{ ctx.stateCode = 404; ctx.send('not found:'+ctx.req.url); }); server.listen(3001);