EventEmitter(前端里特种重要的一个设计模式:发布订阅)
function EventEmitter() {
this._events = {}
}
EventEmitter.prototype.on = function (eventName, callback) {
if (!this._events) this._events = {}
const eventList = this._events[eventName] || (this._events[eventName] = [])
eventList.push(callback)
}
EventEmitter.prototype.emit = function (eventName, ...args) {
if (this._events[eventName]) {
this._events[eventName].forEach(cb => {
cb(...args)
});
}
}
EventEmitter.prototype.off = function (eventName, callback) {
if (this._events[eventName]) {
this._events[eventName] = this._events[eventName].filter(item => (item !== callback && item.cb !== callback))
}
}
EventEmitter.prototype.once = function (eventName, callback) {
if (!this._events[eventName]) this._events[eventName] = []
const once = (...args) => {
callback(...args)
this.off(eventName, once)
}
// 如果once过后,没emit,直接off,那off函数不知道this._events[eventName]实际存的是once,不是callback,所以直接off会失效
// 那我们就在once上关联一下callback,在off函数中增加判断
once.cb = callback
this._events[eventName].push(once)
}
const event = new EventEmitter()
const handle1 = (...msg) => {
console.log(`handle1: ${msg}`);
}
const handle2 = (...msg) => {
console.log(`handle2: ${msg}`);
}
event.on('msg', handle1)
event.once('msg', handle2)
event.off("msg", handle2)
event.emit('msg', "data1", "data2")
event.emit('msg', "new")
// handle1: data1,data2
// handle1: new
koa源码里一个函数的实现(洋葱模型)
// 仿koa源码里 compose 函数
function discount(ctx, next) {
console.log("starting discount");
next(ctx * 0.8)
console.log("ending discount");
}
function num(ctx, next) {
console.log("starting num");
next(ctx / 0.8)
console.log("ending num");
}
function express(ctx, next) {
console.log("starting express");
next(ctx * 10)
console.log("ending express");
}
function compose(args) {
let result
return function (ctx) {
// next 的本质就是下一个函数
const dispatch = function (i, ctx) {
let fn
if (i < args.length) fn = args[i];
if (i === args.length) {
result = ctx
return
}
fn(ctx, (ctx) => dispatch(++i, ctx))
}
dispatch(0, ctx)
return result
}
}
// const c = compose([discount, num, express])
// console.log(c(10));
/**
starting discount
starting num
starting express
ending express
ending num
ending discount
100
*/
async function async_discount(ctx, next) {
console.log("starting discount");
await next(ctx.a = 10)
console.log("ending discount");
// 在这里return ctx的话,下面打印@@res,就回打印出 { a: 20 }
// return ctx
}
async function async_num(ctx, next) {
console.log("starting num");
await next(ctx.a = 20)
console.log("ending num");
}
async function async_express(ctx, next) {
console.log("starting express");
await next(ctx.a = 20)
console.log("ending express");
}
function koa_compose(middleware) {
// middleware is a function array
return function (ctx, next) {
let index = -1
const dispatch = function (i) {
if (i <= index) return Promise.reject(new Error(`next() called multiple times`))
index = i
let fn = middleware[i]
if (i === middleware.length) fn = next
if (!fn) return Promise.resolve()
return Promise.resolve(fn(ctx, dispatch.bind(null, i + 1)))
}
return dispatch(0)
}
}
const c = koa_compose([async_discount, async_num, async_express])
c({}).then(res => {
console.log("@@", res);
})
形形术术的一个东西,比较偏于架构、设计模式(很浅)的一个东西
伪代码:
// import { addStrategy } from "core"; // 通常core为我们的核心代码
// 比作:服务器传来的数据
const serverData = {
bar: {
baz: "barbaz",
show: {
xxx: "barxxx",
yyy: "baryyy"
}
},
foo: {
baz: "foobaz",
show: {
xxx: "fooxxx",
yyy: "fooyyy"
}
},
};
const strategys = []; // 策略
const layouts = []; // 布局
const addStrategy = (layout, strategy) => {
strategys.push(strategy);
layouts.push(layout)
}
const run = () => {
strategys.forEach((strategy, idx) => {
const cb = (...params) => {
layouts[idx].render(...params);
}
strategy(serverData, cb);
})
}
const layoutX = {
name: "layoutX",
render: ({ xxx, yyy }) => (
console.log('rendening..... layoutX', xxx, yyy)
),
methods: {
handleLog: () => {
sendLog();
}
}
}
const layoutY = {
name: "layoutY",
render: ({ xxx, yyy }) => (
console.log('rendening..... layoutY', xxx, yyy)
),
methods: {
handleLog: () => {
sendLog();
}
}
}
addStrategy(layoutX, (ctx, callback) => {
if (ctx.bar && ctx.bar.baz) {
callback(ctx.bar.show)
}
})
addStrategy(layoutY, (ctx, callback) => {
if (ctx.foo && ctx.foo.baz) {
callback(ctx.foo.show)
}
})
run() // run函数也可以return一些东西使用
工具(流程函数)
有时候是能用在处理返回的数据中
-
- 如果你们的 server,每次返回的数据,都是一个前端不可用的
-
- 每次接口的数据都需要处理。但是能不能总结一些共同点
-
-
- 比如下面的代码中,a、b、c就是我们的处理返回数据的函数,rest就是后端返回的数据
-
// 同步
// 参数不传递,且不返回
const syncPipe = (fns) => (rest) => {
return fns.forEach(fn => {
fn(rest)
});
}
// 瀑布型:前一个函数的返回值,是下一个函数的入参
const waterFallAsyncPipe = (fns) => (rest) => {
return fns.reduce((total, item) => item(total), rest)
}
// 熔断型:如果钱一个函数返回某特定值(我们想让它熔断的值,比如undefined),那后面的函数就不执行了
const boilSyncPipe = (fns) => (rest) => {
// 瀑布型的熔断
// return fns.reduce((total, item) => total ? item(total) : total, rest)
return fns.reduce((total, item) => total ? item(rest) : undefined, {})
}
const a = (params) => {
console.log('a', params);
return params + 1;
}
const b = (params) => {
console.log('b', params);
// return params + 1;
return undefined
}
const c = (params) => {
console.log('c', params);
return params + 1;
}
// syncPipe([a, b, c])(123)
// a 123
// b 123
// c 123
// waterFallAsyncPipe([a, b, c])(123)
// a 123
// b 124
// c 125
// boilSyncPipe([a, b, c])(123)
// a 123
// b 124
// 异步
const asyncPipe = (fns) => (rest) => new Promise((resolve, reject) => {
fns.reduce((total, item) => total.then(res => item(rest)).catch(reject),
Promise.resolve(rest))
.then(resolve)
})
// 瀑布型
const asyncWaterFallPipe = (fns) => (rest) => new Promise((resolve, reject) => {
fns.reduce((total, item) => total.then(res => item(res)).catch(reject),
Promise.resolve(rest))
.then(resolve)
})
// 熔断型
const asyncBoilPipe = (fns) => (rest) => new Promise((resolve, reject) => {
fns.reduce((total, item) => total.then(res => res ? item(res) : Promise.resolve(res)).catch(reject),
Promise.resolve(rest))
.then(resolve)
})
const aSync = (params) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('aSync', params);
resolve(params + 1);
}, 1000)
})
}
const bSync = (params) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('bSync', params);
// resolve(params + 1);
resolve(undefined);
}, 1000)
})
}
const cSync = (params) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('cSync', params);
resolve(params + 1);
}, 1000)
})
}
// const aaa = await asyncPipe([aSync, bSync, cSync])(123)
// aSync 123
// bSync 123
// cSync 123
// console.log(aaa); // 124
// const aaa = await asyncWaterFallPipe([aSync, bSync, cSync])(123)
// aSync 123
// bSync 124
// cSync 125
// console.log(aaa); // 126
const aaa = await asyncBoilPipe([aSync, bSync, cSync])(123)
// aSync 123
// bSync 124
console.log(aaa); // undefined