HOME/Articles/

再次理解Promise

Article Outline

再次理解Promise

<!--more-->

对task queue 和 Event loop了解之后,再次理解一下Promise:

Promise是什么

console.dirPromise);( Promise.png

这么一看,Promise是一个对象,更是一个构造函数。自己身上有all、reject、resolve等等,原型上有then、catch等方法。

promise的用法

new一个构造函数的实例出来

  var p1 = new Promise (function (resolve,reject) {
          //做一些异步操作
    setTimeout(function(){
        console.log('执行完成');
        resolve('随便什么数据');
    }, 2000);
  })

Promise的构造函数接收一个参数,是函数,并且传入两个参数:resolve,reject, resolve在fullfiled状态时会执行,reject在Promise状态为rejected执行。

这时候只是new了一个实例出来,但是代码却执行了,所以一般要将Promise实例包在一个函数中,return出来。 需要执行的时候再去调用这个函数。

        function learnPromise () {
            var p = new Promise (function (resolve,reject) {
//                异步操作
                setTimeout(function () {
                    console.log('done')
                    resolve('fulfilled')
                },300)
            })
            return p
        }

这个learnPromise 函数最后return出来一个promise实例,前面输出的Promise原型上的then和catch方法在实例上也可以使用。

        learnPromise().then((msg)=> {
            console.log('现在的Promise状态是:' + msg); // 现在的Promise状态是:fulfilled
        })

这个then方法可以接收俩函数作为参数,一个resolve,一个reject,接收的这俩函数的参数就是learnPromise()中的 resolve('fulfilled')中的'fulfilled',即在learnPromise中调用resolve/reject时传入的参数,同时这个参数在Promise规范中是有限定的,只传一个,不然会拿到undefined,所以有多个参数的话,考虑可以传对象。

promise的优势

上面的then方法中的resolve,reject这些函数,就跟平常的回调函数一样,采用promise的方式,可以把回调的写法分离出来,这样异步操作执行完后,进行链式调用,能在一定程度上避免回调地狱

Promise的优势在于你在then方法里仍旧可以new Promise,继续return出一个promise对象。进行下一轮的then调用:

举例说明:

        var num = 0  //现在有全局变量num,然后有三个return promise对象的函数。
        function pro1 () {
            return new Promise((resolve,reject)=> {
                setTimeout(()=> {
                    num += 1
                    resolve(num)
                },1000)
            })
        }
        function pro2 () {
            return new Promise((resolve,reject)=> {
                setTimeout(()=> {
                    num += 1
                    resolve(num)
                },1000)
            })
        }
        function pro3 () {
            return new Promise((resolve,reject)=> {
                setTimeout(()=> {
                    num += 1

                    resolve(num)
                },1000)
            })
        }

然后我们可以这么连续的进行链式调用:

        pro1()
            .then((num)=> {
                console.log('此时num为:' + num)
                return pro2() // 在每一个then方法的回调中仍然可以return promise对象,所以可以接着then下去
            })
            .then((num)=> {
                console.log('此时num为:' + num)
                return pro3()
            })
            .then((num)=> {
                console.log('此时num为:' + num)
            })

输出为 : (过1000ms) 此时num为:1 (过1000ms) 此时num为:2 (过1000ms) 此时num为:3

这样就实现了链式调用。当然可以直接return 数据而不是promise对象。在后面的then中也能接收到前一个then中return的数据。

catch和reject

上面提到的hen方法中可以接受俩函数作为参数,那么第二个参数就是reject,并且不是必需的。

这个reject也可以写在catch方法中作为参数。其实更推荐这种写法,有个好处是异步任务执行完成后判断为fullfiled然后执行resolve时,这时候如果发生报错(抛出异常),会转而执行catch方法

all 和 race

先来说all,Promise.all()接收一个数组,其中每项都是一个promise对象。all的逻辑是,等所有的异步操作都进行完成之后才进行执行回调,所以then方法中的回调是所有promise对象中的异步操作都完成之后进行的。

而race恰恰相反,最快的那个异步操作执行完成之后,就开始执行then的回调,但是其余没结束的异步操作还会接着执行

先给出三个return promise对象的函数

    function pro1() {
        return new Promise (function (resolve,reject) {
//            异步操作
            var num = 'a'
            setTimeout(() => {
                console.log('异步开始执行')
                resolve(num)
            },300)
        })
    }
    function pro2() {
        return new Promise (function (resolve,reject) {
//            异步操作
            var num = 'b'
            setTimeout(() => {
                console.log('异步开始执行')
                resolve(num)
            },500)
        })
    }
    function pro3() {
        return new Promise (function (resolve,reject) {
//            异步操作
            var num = 'c'
            setTimeout(() => {
                console.log('异步开始执行')
                resolve(num)
            },700)
        })
    }

pro1()异步操作延时为300ms,pro2()500ms,pro3()700ms 先来看Promise.all()的例子:

      Promise.all([pro1(),pro2(),pro3()]).then((res) => {
        console.log(res)
    })

执行结果为:

异步开始执行  // pro1
异步开始执行  // pro2
异步开始执行  // pro3
["a", "b", "c"]

最后all()的then方法中得到的是一个数组res,这个数组中包含了所有异步操作运行的结果

再来看Promise.race()的例子:

    Promise.race([pro1(),pro2(),pro3()]).then((res) => {
        console.log(res)
    })

执行结果为:

异步开始执行 //pro1
a // pro1的then中的回调执行的结果
异步开始执行 //pro2
异步开始执行 //pro3

race()是最快完成的异步任务完成之后就开始执行then中的回调,但是其他没完成的异步操作还是会继续执行