如何用 Promise 解决回调地狱

1,903 阅读2分钟

如何用 Promise 解决回调地狱

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

导语

上文了解什么是回调地狱,如何使用thunk函数解决回调地狱,具体可参考上文: JS 如何用 thunk 函数解决回调地狱?。解决回调地狱的方法有很多,本文讨论如何使用 Promise来解决回调地狱。

写一个回调地狱

const fn = (str, callback) => {
    setTimeout(() => {
        console.log(str)
        callback(str)
    }, 100);
}

fn('1', () => {
    fn('2', () => {
        fn('3', () => {
            fn('4', () => {
                console.log('done')
            })
        })
    })
})

这里的 fn调用形成了一个喜闻乐见的回调地狱。运行结果:

1
2
3
4
done

目的是在异步函数fn中,依次传递参数 1-4,并且依次去执行。

构造 Promise 工厂函数

const promiseFactory = (str) => {
    return new Promise((resolve) => {
        fn(str, resolve)
    })
}

原理很简单,主要目的就是将 参数和回调进行分离,这也是解决回调地狱的基本原则。通过 promise 工厂函数,接受除了 callback意外的所有参数,然后利用 promise 的 resolve 替代原有的 callback去调用原函数。这样就完成了一个 promise 工厂函数。

链式调用工厂函数

promiseFactory('1')
    .then(() => promiseFactory('2'))
    .then(() => promiseFactory('3'))
    .then(() => promiseFactory('4'))
    .then(() => { console.log('done') })

运行结果:

1
2
3
4
done

和原回调地狱方式调用的结果是完全一致的,实际上他们的执行顺序也是一致的

完整代码

const fn = (str, callback) => {
    setTimeout(() => {
        console.log(str)
        callback(str)
    }, 100);
}

// fn('1', () => {
//     fn('2', () => {
//         fn('3', () => {
//             fn('4', () => {
//                 console.log('done')
//             })
//         })
//     })
// })

const promiseFactory = (str) => {
    return new Promise((resolve) => {
        fn(str, resolve)
    })
}

promiseFactory('1')
    .then(() => promiseFactory('2'))
    .then(() => promiseFactory('3'))
    .then(() => promiseFactory('4'))
    .then(() => { console.log('done') })

多参数的回调地狱

const fn = (greeting, myName, callback) => {
    setTimeout(() => {
        console.log(greeting + ',' + myName)
        callback()
    }, 100);
}

// fn('1', () => {
//     fn('2', () => {
//         fn('3', () => {
//             fn('4', () => {
//                 console.log('done')
//             })
//         })
//     })
// })

const promiseFactory = (greeting, myName) => {
    return new Promise((resolve) => {
        fn(greeting, myName, resolve)
    })
}

promiseFactory('hehe', 'ouda')
    .then(() => promiseFactory('hello', 'ouda1'))
    .then(() => promiseFactory('hi', 'ouda2'))
    .then(() => promiseFactory('haha', 'ouda3'))
    .then(() => { console.log('done') })

运行结果:

hehe,ouda
hello,ouda1
hi,ouda2
haha,ouda3
done

在多参数的环境下同样适用,原则就是将 callback以外的函数重新封装成工厂函数,然后将 promise 的 resolve 替代 callback即可。

最终我们成功地可以将原来的嵌套回调,修改为了链式调用,这也是在我们开发中经常会用到的解决回调地狱的一个惯用手段。