正井猫

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

"渣男"之Promise

一.为"渣男"正名

  在之前写过一篇《NodeJS回调地狱》的文章,文章最后提到过使用Promise和async来解决"回调地狱"的问题,本文主要讲解Promise。如标题所示,我为什么把Promise叫做 "渣男"呢,从字面意思来看,Promise是承诺的意思,就是现在我给不了你结果,但是在未来我会给你一个结果,听起来就像一个渣男的言辞,这只是笔者打了个比方而已,请各位读者不要往心里去。那么我们言归正传,正是因为Promise的这种行为方式,就可以很好的解决回调地狱的问题,听起来可能有点懵,当你看完全文可能就不会有这种感觉了。

二.Promise状态机

  Promise除了是个"渣男"外,它还是一个状态机,总共有三种状态: pending、resolved、rejected。pending是待决状态,说白了就是Promise还未给出任何的结果,即 "渣男"还在犹豫;resolved就是给出正确的结果,"渣男"给出一个好的结果,那么他就不是一个"渣男";rejected就是给出错误的结果,那么他就是一个名副其实的 "渣男"。我们通过代码来解释一下这三种状态

function promTest() {
    /**
     * Promise的构造方法接收一个方法,方法有两个参数,第一个参数
     * 用于返回正确的结果;第二个参数用于返回错误的结果;两个参数均为方法
* 只能接收一个参数
*/ return new Promise((resolve, reject) => { setTimeout(() => { resolve('success') }, 200) }) } let promise = promTest() //此时promTest中的Promise并没有给出结果, 此时的状态为pending console.log(promise) /** * promTest中过了200ms, Promise才给出结果, 这里过 * 300ms再次打印promise, 此时promise的状态为resolved */ setTimeout(() => { console.log(promise) }, 300)

  在chrome控制台中执行的结果如下图:

                                                                                      

   接下来将代码promTest方法中的resolve('success')改为 reject(‘’fail),那么在chrome控制台的输出入下:              

                                                                                     

三.Promise异步编程

  对Promise的执行then或者catch操作会返回一个新的Promise,该Promise最终状态根据then和catch的回调函数的执行结果决定,遵循着以下三个原则:

    A.如果回调函数最终是throw,该Promise是rejected状态

        B.如果回调函数最终是return,该Promise是resolved状态

        C.如果回调函数最终retuen一个Promise,该Promise会和回调函数return的Promise状态保持一致

  接下来,我们对三个原则分别展开阐述

3.1.回调函数throw

function promTest() {
    /**
     * Promise的构造方法接收一个方法,方法有两个参数,第一个参数
     * 用于返回正确的结果;第二个参数用于返回错误的结果
     */
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('success')
        }, 200)
    })
}

let promise = promTest()

//因为promise必然进入then方法,那么在该回调函数中
//抛出异常,promise2也是一个Promise,状态为rejected
let promise2 = promise 
            .then(res => {
                //抛出异常
                throw new Error('fail')
            })

// promise和promise2的状态均为pending
console.log(promise)
console.log(promise2);

setTimeout(() => {
    console.log(promise)
// promise2的状态得根据promse回调函数then来决定,而在then中直接抛出异常
console.log(promise2)
}, 300)

  chrome的执行结果如下:

                                                                                

3.2.回调函数return

function promTest() {
    /**
     * Promise的构造方法接收一个方法,方法有两个参数,第一个参数
     * 用于返回正确的结果;第二个参数用于返回错误的结果
     */
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('fail')
        }, 200)
    })
}

let promise = promTest()

//因为promise必然进入catch方法,但是在catch回调
//函数中直接return一个值,promise2的状态为resolved
let promise2 = promise 
            .catch(error => {
                return 'fail' 
            })

// promise和promise2的状态均为pending
console.log(promise)
console.log(promise2);

setTimeout(() => {
    console.log(promise)
    console.log(promise2)
}, 300)

  chrome的执行结果如下:

                                                                                

3.3.回调函数return一个Promise

function promTest() {
    /**
     * Promise的构造方法接收一个方法,方法有两个参数,第一个参数
     * 用于返回正确的结果;第二个参数用于返回错误的结果
     */
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('success')
        }, 200)
    })
}

let promise = promTest()

// 因为promise必然进入then方法,但是在then回调
// 函数中直接return一个Promise,promise2的状态
// 与回调函数中的返回的Promise的状态一致
let promise2 = promise 
            .then(res => {
                return new Promise((resolve, reject) => {
                    reject('fail')
                })
            })

// promise和promise2的状态均为pending
console.log(promise)
console.log(promise2);

setTimeout(() => {
    console.log(promise)
    console.log(promise2) // promise2的状态为reject
}, 300)

  chrome的执行结果如下:

                                                                                

四.Promise解决回调地狱问题

  我们还是沿用《NodeJS回调地狱》中的那个面试案例来说(如果不知道那个案例的,建议点击链接过去看看),我们通过Promise来改造一下,代码如下所示:

// 面试函数
function interview(round) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if(Math.random() > 0.5) {
                resolve('success')
            }else {
                let error = new Error('fail')
                error.round = round
                reject(error)
            }
        }, 200)
    })
}

interview(1)  //第一轮面试
    .then(res => {
        /**
         * interview返回一个Promise, 会得到一个新的Promise,
         * 新的Promise的状态和interview(2)的状态一致
         */
        console.log('第一轮面试通过')
        return interview(2) 
    }).then(res => {
        console.log('第二轮面试通过')
        return interview(3)
    }).then(res => {
        console.log('面试全部通过')
    }).catch(error => { 
        console.log('第 ' + error.round + ' 轮面试失败.')
    })

  chrome的执行多次的结果如下:

                                                

五.Promise并发执行

  现将上面的案例改一下,例如现在去两家公司面试,那么该如何处理呢?

// 面试函数
function interview(name) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if(Math.random() > 0.4) {
                resolve('success')
            }else {
                let error = new Error('fail')
                error.name = name
                reject(error)
            }
        }, 200)
    })
}

Promise.all([
    interview('baidu'),
    interview('alibaba')
]).then(res => {
    console.log('两家公司均面试成功.')
}).catch(error => {  //谁先reject,谁先进入该回调函数
    console.log('面试失败在 ' + error.name + ' 公司上');
})

    执行的结果各位读者可以自行测试,我这里就不展示了。

 

posted on 2020-02-18 17:59  正井猫  阅读(446)  评论(0编辑  收藏  举报