浅浅入架构、设计模式 (17)

52 阅读4分钟

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

形形术术的一个东西,比较偏于架构、设计模式(很浅)的一个东西

image.png

伪代码:

// 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