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