js多個異步請求,按順序執行next


在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);

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM