Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save starandtina/c2b2fa0014f259e199fdd563374ec998 to your computer and use it in GitHub Desktop.
Save starandtina/c2b2fa0014f259e199fdd563374ec998 to your computer and use it in GitHub Desktop.
interview

实现一个事件总线,要求满足一下特性:

  1. 简单的回调注册和事件触发。
// 注册
bus.on('event', function listener1(arg1, arg2) { ... } )
// 触发
bus.trigger('event', arg1, arg2)
  1. 同一事件下,回调可以指定顺序。要求有检测错误的能力。
bus.on('event', function listener1() { ... } )
bus.on('event', {
    fn: function listener2() { ... }
    before: ['listener1']
})
  1. 之间回调中可以再触发事件,事件完整结束后能打印出完整调用栈,异步调用栈信息也要打印。
bus.on('event1', function() {
    // 以下只是伪码,具体 api 可以根据需要自行设计。
   bus.tigger('event2')
   new Promise(function(resolve){
      setTimeout(function(){
        bus.trigger('event3')
      }, 1000)
   })
})

bus.on('event2', function() {
    // do anything
    bus.trigger('event4')
})

result = bus.trigger('event1')
result.then(function( callStack ){
    // 要求在 callStack 中正确描述 event1 回调中同步触发了 event2 ,异步触发了 event3。
    // 同时要描述 event2 中触发了 event4。
})

====================================== 附加题:实现一个简单的类 graphQL parser。

  1. 简单查询
const inputStr = `User {
  id,
  name,
  age
}`
console.log( parse(inputStr) )
/*
期望输出:
{
  type: 'User',
  fields : ['id','name','age']
}
*/
  1. 查询和关联查询
const inputStr = `User( age:21 ){
  id,
  name,
assigned Task( content: 'run' ): {
    id,
    content
  }
}`
console.log( parse(inputStr) )
/*
期望输出:
{
  type : 'User',
  attrs : {
    data : {
      age :21
    }
  },
  fields : ['id', 'name'],
  relations : {
    'assigned Task' : {
      name : 'assigned',
      target : {
        type : 'Task',
        attrs : {
          data : {
            content : 'run'
          }
        },
        fields : ['id','content']
      }
    }
  }
}
*/
@starandtina
Copy link
Author

starandtina commented Sep 9, 2022

const bus = {
  events: {},
  on(event, option) {
    // {event: [{fn, before}, {fn, before}, ...]}
    this.events[event] = this.events[event] ?? [];

    let fn;
    let before = [];

    if (typeof option === "object") {
      fn = option.fn;
      before = option.before ?? [];
    } else {
      fn = option;
    }

    // 先找到 before 中最靠前的位置
    let beforeMinIndex = Number.MAX_SAFE_INTEGER;
    for (let i = 0; i < before.length; i++) {
      for (let j = 0, len = this.events[event].length; j < len; j++) {
        const listener = this.events[event][j];

        if (before.includes(listener.fn.name) && beforeMinIndex >= j) {
          beforeMinIndex = j;
        }
      }
    }
    console.log(beforeMinIndex)
    // 然后再插入
    if (beforeMinIndex !== Number.MAX_SAFE_INTEGER) {
      this.events[event].splice(beforeMinIndex, 0, { fn, before });
    } else {
      this.events[event].push({ fn, before });
    }
  },
  trigger(event, ...args) {
    this.events[event].forEach((option) => option.fn(...args));
  }
};

// 1. 简单的回调注册和事件触发。
// 注册
// bus.on("event", function listener1(arg1, arg2) {
//   console.log("event", arg1, arg2);
// });
// // 触发
// bus.trigger("event", 1, 2);

// 2. 同一事件下,回调可以指定顺序。要求有检测错误的能力。
bus.on("event", function listener1() {
  console.log("listener1");
});
bus.on("event", {
  fn: function listener2() {
    console.log(arguments.callee.name, this.before);
  },
  before: ["listener1"]
});
bus.on("event", {
  fn: function listener3() {
    console.log(arguments.callee.name, this.before);
  },
  before: ["listener2"]
});

bus.on("event", {
  fn: function listener4() {
    console.log(arguments.callee.name, this.before);
  },
  before: ["listener3", "listener2"]
});
bus.trigger("event");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment