Promise

作者: vren | 来源:发表于2020-11-24 21:14 被阅读0次

    promise是什么?

    上手直接console.dir(Promise)出来看看promise到底是什么


    Promise

    咱们dir出来Promise是一个构造函数,本身上面有reject、resolve这几个眼熟的方法,原型上有then、catch等同样很眼熟的方法。所以说用Promise new出来的对象肯定就有then、catch方法了

    我课堂笔记在OneNote上写了Promise
    1.主要用于异步计算
    2.可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
    3.可以在对象之间传递和操作promise,帮助我们处理队列

    既然主要用于异步计算,那我们new一个试试:

        var p = new Promise((resolve, reject) => {
            // 执行异步操作试试
            setTimeout(() => {
                console.log('到点执行完成了');
                resolve('随便执行了什么')
            }, 2000);
        })
    

    Promise的构造函数接收一个参数,是函数,并且传入两个固定参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。其实这里用“成功”和“失败”来描述并不准确,按照标准来讲,resolve是将Promise的状态置为fullfiled,reject是将Promise的状态置为rejected。不过在我们开始阶段可以先这么理解,后面再细究概念。

    在上面的代码中,我们执行了一个异步操作,也就是setTimeout,2秒后,输出“执行完成”,并且调用resolve方法。

    不过,要注意的是,我们只是new了一个对象,并没有调用它,而传进去的参数就已经执行了,所以这告诉我们在用Promise的时候一般都是要申明一个函数包裹,在需要用的时候再去运行这个函数。

        function runAsync() {
            var p = new Promise((resolve, reject) => {
                // 执行异步操作试试
                setTimeout(() => {
                    console.log('到点执行完成了');
                    resolve('随便执行了什么')
                }, 2000);
            })
            return p;
        }
        runAsync()
    

    这时在看控制台打印的结果


    为什么只在控制台打印了“到点执行完成了”,为什么一定要包装成函数呢?请看:

        runAsync().then(function(data) {
            console.log(data);
        })
    

    因为我们封装的runAsync函数,会return出Promise对象,可以直接调回then方法,会拿到我们用runAsync中调用resolve时传的参数。在运行这段代码后,会在2秒后输出“到点执行完成了”,紧接着输出“随便执行了什么”。
    所以,then里面的函数就跟我们平时的回调函数一个意思,能在runAsync这个异步任务执行完之后被执行,简单来说,就是把原来的回调写法分离出来了,在异步操作执行完成之后,利用链式调用的方式执行回调函数。

    如果这时你不屑一顾的想到,那么牛逼轰轰的Promise就这点能耐?我把回调函数封装一下,给runAsync传进去不也一样吗,就像这样:

        function runAsync(callback) {
            setTimeout(function() {
                console.log('到点执行完成了');
                callback('随便执行了什么');
            }, 2000);
        }
    
        runAsync(function(data) {
            console.log(data);
        });
    

    效果也是一样的呀,还费劲用Promise干嘛。那么问题来了,有多层回调该怎么办?如果callback也是一个异步操作,而且执行完后也需要有相应的回调函数,该怎么办呢?总不能再定义一个callback2,然后给callback传进去吧。而Promise的优势在于,可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作。

    链式操作的用法

    所以,从表面上看,Promise只是能够简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多。所以使用Promise的正确场景是这样的:

        function runAsync1() {
            var p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log('打印了1');
                    resolve('输出1')
                }, 2000);
            })
            return p;
        }
    
        function runAsync2() {
            var p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log('打印了2');
                    resolve('输出2')
                }, 2000);
            })
            return p;
        }
    
        function runAsync3() {
            var p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log('打印了3');
                    resolve('输出3')
                }, 2000);
            })
            return p;
        }
    runAsync1()
    .then(function(data){
        console.log(data);
        return runAsync2();
    })
    .then(function(data){
        console.log(data);
        return runAsync3();
    })
    .then(function(data){
        console.log(data);
    });
    
    

    运行结果:


    在then方法中,你也可以直接return数据而不是Promise对象,在后面的then中可以接受数据:

        runAsync1()
            .then(function(data) {
                console.log(data);
                return runAsync2();
            })
            .then(function(data) {
                console.log(data);
                return '直接返回数据'; //这里直接返回数据
            })
            .then(function(data) {
                console.log(data);
            });
    

    reject的用法

    到这里,你应该对“Promise是什么玩意”有了最基本的了解。那么我们接着来看看ES6的Promise还有哪些功能。我们光用了resolve,还没用reject呢,它是做什么的呢?事实上,我们前面的例子都是只有“执行成功”的回调,还没有“失败”的情况,reject的作用就是把Promise的状态置为rejected,这样我们在then中就能捕捉到,然后执行“失败”情况的回调。看下面的代码。

        function getNum() {
            var p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    let num = Math.ceil(Math.random() * 10)
                    if (num <= 5) {
                        resolve(num)
                    } else {
                        reject('数字太大了');
                    }
                }, 2000);
            })
            return p;
        }
        getNum().then(
            function(data) {
                console.log(data);
            },
            function(reason) {
                console.log(reason);
            }
        )
    

    getNumber函数用来异步获取一个数字,2秒后执行完成,如果数字小于等于5,我们认为是“成功”了,调用resolve修改Promise的状态。否则我们认为是“失败”了,调用reject并传递一个参数,作为失败的原因。
    运行getNumber并且在then中传了两个参数,then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。所以我们能够分别拿到他们传过来的数据。

    catch的用法

    除了then之外,还有catch方法,和then的第二个参数一样,也是用来指定reject的回调:

        getNum().then((data) => {
            console.log(data);
        }).catch((reason) => {
            console.log(reason);
        })
    

    既然then方法中可以传递两个参数,那么这样写有什么好处呢,请看下面函数:

        getNum().then((data) => {
            console.log(data);
            console.log(resolve); // 此处的resolve没有定义
        }).catch((reason) => {
            console.log(reason);
        })
    

    在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。

    相关文章

      网友评论

          本文标题:Promise

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