su37josephxia / frontend-interview

前端面试知识点
MIT License
159 stars 45 forks source link

解释一下事件如何解决回调地狱 #118

Open su37josephxia opened 2 years ago

RJM1996 commented 2 years ago

如何使用事件解决回调地狱?

其实 Promise 就是一个例子,写在 then 方法中的回调函数就相当于在监听着 Promise 的 resolve 事件,当 resolve 事件触发时,then 中的回调函数就执行

为了防止回调函数层层嵌套,我们可以使用一个 EventBus

例如我们有两个请求,请求2依赖请求1,先在 eventBus 中注册某个事件,事件处理函数为请求2

当请求 1 执行完毕后,在其回调函数中触发 eventBus 的这个事件,则此时请求2就会被触发,这样就避免了请求2嵌套在请求1的回调函数中,避免了回调地狱的形成

function test1() {
  function request(req, cb) {
    setTimeout(function () {
      console.log(req)
      cb && cb(req + 1)
    })
  }

  function EventBus() {
    this.cb = null
    // 触发事件
    this.trigger = (res) => {
      this.cb(res)
    }
    // 监听事件
    this.listener = (cb) => {
      this.cb = cb
    }
  }

  const eventBus = new EventBus()

  eventBus.listener((param) => {
    request(param, (res) => {
      console.log(res)
    })
  })
  request(1, (res) => {
    eventBus.trigger(res)
  })

}
test1()
zcma11 commented 2 years ago

解决回调地狱的方法是将回调地狱内的逻辑提取出来。而事件能做到注册回调,触发回调。所以事件解决回调地狱的方法就是将回调单独注册,再原本回调地狱的地方修改成派发事件。

emitter.on('action1', (args) => {
  emitter.emit('action2', args)
})
emitter.on('action2', (args) => {
  emitter.emit('action3', args)
})
emitter.on('action3', (args) => {
  emitter.emit('completed', args)
})
emitter.on('completed', (args) => {
  // do something
})

fs.readFile('', (err, data) => {
  emitter.emit('action1', data)
})

或者像 koa 的洋葱模型那样。

ruixue0702 commented 2 years ago

如何使用事件解决回调地狱? // 回调地狱:是因为想要实现异步事件能够顺序执行而产生一种函数层层嵌套调用的现象

// 定义四个函数,每个函数中执行一个异步事件,异步事件完成后调用 done 函数 const fn1 = () => { // 异步事件1 done() }

const fn2 = () => { // 异步事件2 done() } const fn3 = () => { // 异步事件3 done() } const fn4 = () => { // 异步事件4 done() }

// 将所有的异步事件的名称放到数组中 const list = [ 'fn1', 'fn2', 'fn3', 'fn4'] // 定义一个索引变量 i let i = -1 // 每个异步事件执行完成需要调用 done // 在done函数中,先对索引值做+1操作 // 再在list数组中取出索引位置上的函数名称,并执行 const done = () => { i++ list[i]() }

// 执行fn1 fn1()