Javascript回调地狱的解决方案

1,587 阅读2分钟

「这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战」。

什么是回调地狱

一个函数作为参数在方法中调用时,这个函数就叫做叫做回调函数,如果在回调函数中再传入一个函数,层层嵌套回调,这样就会形成回调地狱

回调地狱是一种不好的编码习惯,可读性差并且难以维护,比如下面的代码

const process = (msg, cb) => {
  setTimeout(() => {
    console.log(msg)
    cb()
  }, 1000)
}
process('msg 1', () => {
  process('msg 2', () => {
    process('msg 3', () => {
      process('msg 4', () => {
          console.log('end')
        })
    })
  })
})

如何解决回调地狱

解决回调地狱的思路就是要把一层层嵌套的回调函数“解救”出来,把多层的结构给他“拍平”

Thunk函数

还不知道什么是Thunk函数 阮一峰 Thunk 函数的含义和用法

Thunk函数是一个比较“古老”的概念,上个世纪60年代就诞生了,在Javascript中其实是结合柯里化的一种应用,可以将多参数版本的函数转换为单参数,并且在返回的新函数中只接受回调函数作为参数。

  • 将上面的例子修改,利用Thunk函数加上一个执行函数就可以从回调地狱中解救出来,只需要维护执行顺序的数组即可
const process = (msg, cb) => {
  setTimeout(() => {
    console.log(msg)
    cb && cb()
  }, 1000)
}
// thunk函数
const thunk = (msg) => (cb) => process(msg, cb)

const p1 = thunk('msg 1')
const p2 = thunk('msg 2')
const p3 = thunk('msg 3')
const p4 = thunk('msg 4')

// 执行顺序
const arr = [p1, p2, p3, p4]

// 执行函数
const gen = (arr) => () => arr.reduceRight((a, b) => () => b(() => a()))()

const run = gen(arr)
run()

Promise

采用链式操作,按照顺序调用,在 then 中,会等待前一个 Promise 的状态变成 Fulfilled 再执行,返回的也是一个 Promise 对象

  • 将之前的例子用 Promise 包一层,可以看到改用链式调用会非常之优雅
const process = (msg, cb) => {
  setTimeout(() => {
    console.log(msg)
    cb && cb()
  }, 1000)
}

const processPromise = msg => {
  return new Promise((resolve, reject) => {
    process(msg, resolve)
  })
}

processPromise('msg 1')
  .then(_ => processPromise('msg 2'))
  .then(_ => processPromise('msg 3'))
  .then(_ => processPromise('msg 4'))

async/await

async/awaitPromise 的一种语法糖,会默认的返回一个 Promise 对象 resolve 的值,可以直接进行 then 操作,可以将 Promise 的链式操作改为顺序执行(异步等待)

const run = async() => {
  await processPromise('msg 1')
  await processPromise('msg 2')
  await processPromise('msg 3')
  await processPromise('msg 4')
}

run()