美文网首页前端秘境
Promise链式调用解决回调地狱

Promise链式调用解决回调地狱

作者: 做梦跳绳闪到腰 | 来源:发表于2019-06-27 11:47 被阅读68次

    什么 回调地狱

    异步JavaScript,或者说使用回调的JavaScript,很难直观地得到正确的结果。很多代码最后看起来都是这样的:

    fs.readdir(source, function (err, files) {
        if (err) {
            console.log('Error finding files: ' + err)
        } else {
            files.forEach(function (filename, fileIndex) {
                console.log(filename)
                gm(source + filename).size(function (err, values) {
                    if (err) {
                        console.log('Error identifying file size: ' + err)
                    } else {
                        console.log(filename + ' : ' + values)
                        aspect = (values.width / values.height)
                        widths.forEach(function (width, widthIndex) {
                            height = Math.round(width / aspect)
                            console.log('resizing ' + filename + 'to ' + height + 'x' + height)
                            this.resize(width, height).write(dest + 'w' + width + '_' + filename, function (err) {
                                if (err) console.log('Error writing file: ' + err)
                            })
                        }.bind(this))
                    }
                })
            })
        }
    })
    

    看到金字塔形状和最后所有的 }) 王大发!这被亲切地称为 回调地狱

    Promise 简介

    所谓Promise,简单来说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。缺少场景支撑,对于小白而言,很难理解Promise的意义。

    《你不知道的JavaScript》中有个场景介绍得很形象

    我走到快餐店的柜台,点了一个芝士汉堡。我交给收银员1.47美元。通过下订单并付款,我已经发出了一个对某个值(就是那个汉堡)的请求。我已经启 动了一次交易。

    但是,通常我不能马上就得到这个汉堡。收银员会交给我某个东西来代替汉堡:一张带有 订单号的收据。订单号就是一个 IOUI owe you, 我欠你的)承诺(promise),保证了最 终我会得到我的汉堡。

    所以我得好好保留我的收据和订单号。我知道这代表了我未来的汉堡,所以不需要担心, 只是现在我还是很饿!

    在等待的过程中,我可以做点其他的事情,比如给朋友发个短信:“嗨,要来和我一起吃 午饭吗?我正要吃芝士汉堡。”

    我已经在想着未来的芝士汉堡了, 尽管现在我还没有拿到手。 我的大脑之所以可以这么 做,是因为它已经把订单号当作芝士汉堡的占位符了。从本质上讲,这个占位符使得这个 值不再依赖时间。这是一个未来值。

    终于, 我听到服务员在喊“订单 113” , 然后愉快地拿着收据走到柜台, 把收据交给收银 员,换来了我的芝士汉堡。

    换句话说, 一旦我需要的值准备好了, 我就用我的承诺值(value-promise)换取这个值 本身。

    但是,还可能有另一种结果。他们叫到了我的订单号,但当我过去拿芝士汉堡的时候,收 银员满是歉意地告诉我:“不好意思,芝士汉堡卖完了。”除了作为顾客对这种情况感到愤 怒之外,我们还可以看到未来值的一个重要特性:它可能成功,也可能失败。

    每次点芝士汉堡,我都知道最终要么得到一个芝士汉堡,要么得到一个汉堡包售罄的坏消息,那我就得找点别的当午饭了。

    所以Promise的出现其实是作为异步编程的一种解决方案。比传统的解决方案-回调函数和事件-更加合理、强大。

    Promise 的基本用法

    var p1 = new Promise((resolve, reject) => {
        setTimeout(resolve, 1000, 'done');
    })
    p1.then(data => {
        console.log(data); // done
    })
    

    Promise一个明显的好处便是可以用来解决回调地狱。特别是在处理多个回调相互依赖的情况。

    使用 Promise 解决多个异步依赖调用,分两种情况

    p1、p2、p3的状态都是resolve的时候,Promise.all的状态才会变成resolve
    只要p1、p2、p3中有一个的状态为reject,那么Promise.all的状态就会变成reject
    所以我们可以用Promise.all()来解决多个异步依赖调用。
    比如我们平常经常遇到的一种情况:
    网站中需要先获取用户名,然后再根据用户名去获取用户信息。这里获取用户名getUserName()和获取用户信息getUser()都是调用接口的异步请求。在获取用户信息之前,需要先获得用户名。也就是说getUser依赖于getUserName的状态。所以我们可以将这两个请求通过Promise.all()封装成一个新的Promise对象。

    function getUserPromise(promiseX, promiseY) {
        return Promise.all([promiseX, promiseY])
            .then(values =>
                // 返回的values由 promiseX 与 promiseY返回的值所构成的数组。
                values
            )
    }
    function getUserName() {
        let data = 'superman';
        return new Promise((resolve, reject) => {
            setTimeout(resolve(data), 1000);
        })
    }
    function getUser() {
        let data = {
            id: 1,
            username: 'superman',
            gender: 'male'
        }
        return new Promise((resolve, reject) => {
            setTimeout(resolve(data), 2000);
        })
    }
    getUserPromise(getUserName(), getUser())
        .then(data => {
            // 这里的data就是包含了getUserName 和 getUser返回值所组成的数组
            console.log(data); // [ 'superman', { id: 1, username: 'superman', gender: 'male' } ]
        })
    

    使用 Promise 的链式调用

    function getUserName() {
        let data = 'superman';
        return new Promise((resolve, reject) => {
            setTimeout(resolve(data), 4000);
        })
    }
    function getUser(username) {
        let data = {
            id: 1,
            username: 'superman',
            gender: 'male'
        }
        return new Promise((resolve, reject) => {
            if (username) {
                setTimeout(resolve(data), 2000);
            }
            else {
                reject('err');
            }
        })
    }
    getUserName().then(username => {
        return getUser();
    })
        .then(user => {
            console.log(user);
        })
        .catch(err => {
            console.log(err);
        })
    

    有了Promise的链式调用,再也不同担心回调地狱的问题了。

    总结

    以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

    相关文章

      网友评论

        本文标题:Promise链式调用解决回调地狱

        本文链接:https://www.haomeiwen.com/subject/zajlcctx.html