【ES6】Promise的使用

Promise

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

1. Promise的状态

笼统的来讲,promise有两种状态

  • pending状态:执行时状态,promise一旦创建,就会执行进入pending状态
  • fulfilled状态:promise执行结束后,状态发生了改变,就会变为fulfilled状态

代码验证:

    const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1)
        }, 0);
    })
    console.log(promise); // Promise {}
    setTimeout(() => {
        console.log(promise); // Promise {: 1}
    }, 500);

上面代码创建一个promise实例,模拟执行异步操作,同步的打印第一个promise对象,所以结果为promise为执行时(pending)状态;当promise执行完毕后,我们创建了一个setTimeout延时操作,延迟时间为500ms,此时promise肯定已经执行完毕了,所以再打印出promise,可以看到,此时的promise为执行完毕(fulfilled)状态。

fulfilled也可以细分为resolvedrejected状态,分别表示成功和失败的状态。

2. Promise特点

promise有两大特点:

  1. 对象的状态不受外界影响。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。

使用promise的好处

  1. 指定回调函数的方式更加灵活:

    旧的:必须在启动异步任务前指定回调函数

    promise:启动异步任务 => 返回promise对象 => 给promise对象绑定回调函数(甚至可以在异步任务之后)

  2. 支持链式调用,可以解决回调地狱问题

    回调地狱:回调函数嵌套调用,外部回调函数异步执行的结果是在嵌套的回调函数执行的条件

    回调地狱缺点:不便于阅读 / 不便于处理异常

    解决方案:promise链式调用

    终极解决方案:async / await

当然,promise也有缺点:

  1. 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  2. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  3. 当处于pending状态时,无法得知目前进展到哪一个阶段。

3. Promise常用API方法

3.1 基本使用

    // 1. 创建一个Promise对象
    const p = new Promise((resolve, reject) => { // 执行器函数
        // 2. 执行异步任务
        setTimeout(() => {
            const time = +new Date()
            if (time % 2 === 0) {
                // 3.1 如果成功了,调用resolve(value)
                resolve('成功了,' + time)
            } else {
                // 3.2 如果失败了,调用reject(reason)
                reject('失败了,' + time)
            }
        }, 1000);
    })

    p.then(value => { // 接收得到成功的value数据  onResolved
        console.log(value);
    }, reason => { // 接手得到失败的reason数据  onRejected
        console.log(reason);
    })

3.2 Promise.resolve()

函数方法,返回一个成功的promise对象

    const p = Promise.resolve(1)
    p.then(value => {
        console.log(value); // 1
    })
    // 相当于
    cosnt p = new Promise((resolve, reject) => {
        resolve(1)
    })

3.3 Promise.reject()

函数方法,返回一个失败的promise对象

    const p = Promise.reject(2)
    p.catch(reason => {
        console.log(reason); // 2
    })
    // 相当于
    const p = new Promise((resolve, reject) => {
        reject(2)
    })

3.4 Promise.all(iterable)

接收的参数为一个数组或者是一个实现iterator接口的参数,每个元素是一个promise实例

在执行过程中,如果数组中所有的promise对象执行完成后的状态都是resolved,则会回调then的成功方法;如果有一个promise对象的执行结果为rejected状态,则会回调失败的回调

成功:各个promise的成功结果将会以数组的形式回调成功函数onResoloved
失败:调用onRejected函数

成功示例

    const p1 = Promise.resolve(1)
    const p2 = Promise.resolve(2)
    const p3 = Promise.resolve(3)
    Promise.all([p1, p2, p3]).then(value => {
        console.log(value); // [1,2,3]
    }).catch(reason => {
        console.log('reason', reason); // 不会执行,因为所有结果都成功了
    })

失败示例

    const p1 = Promise.resolve(1)
    const p2 = Promise.resolve(2)
    const p3 = Promise.reject(3)
    Promise.all([p1, p2, p3]).then(value => {
        console.log('value', value);   // 不会执行
    }).catch(reason => {
        console.log('reason', reason); // 3
    })

3.5 Promise.allSettled(iterable)

接收的参数为一个数组或者是一个实现iterator接口的参数,每个元素是一个promise实例

无论参数中的promise对象成功与否,都会返回成功的回调

    const p1 = Promise.resolve(1)
    const p2 = Promise.resolve(2)
    const p3 = Promise.resolve(3)
    const p4 = Promise.reject(4)

    Promise.allSettled([p1, p2, p3, p4]).then(value => {
        console.log('value', value);
        // 结果 是一个数组
        // 0: {status: "fulfilled", value: 1}
        // 1: {status: "fulfilled", value: 2}
        // 2: {status: "fulfilled", value: 3}
        // 3: {status: "rejected", reason: 4}
        // length: 4
    }).catch(reason => {
        console.log('reason', reason);
    })

3.6 Promise.race(iterable)

方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

只要有一个promise对象状态改变,就会被返回

    const p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1)
        }, 3000);
    })

    const p2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(2)
        }, 1000);
    })

    const p3 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3)
        }, 2000);
    })

    Promise.race([p1, p2, p3]).then(value => {
        console.log('value', value);
    }).catch(reason => {
        console.log('reason', reason); // 由于p2用时时间最短,且是第一个状态发生改变的,所以执行它的结果
    })

3.7 Promise.prototype.then(onResolved, onRejected)

then方法接收两个回调函数,第一个是promise的成功回调,第二个是promise的失败回调函数。

    Promise.resolve(1)
        .then(value => {
            console.log('onResolved', value); // onResolved 1
        }, reason => {
            console.log('onRejected', reason);
        })

3.8 Promise.prototype.catch(reason)

很显然,catch就像try...catch...中的catch一样,能捕捉到上游发生的错误

    Promise.reject(1)
        .then(value => {
            console.log('onResolved', value);
        })
        .catch(reason => {
            console.log('onCatch', reason); // onCatch 1
        })

如果上游的错误已经被处理了,那么catch方法就不会捕获到

    Promise.reject(1)
        .then(value => {
            console.log('onResolved', value);
        }, reason => {
            console.log('onRejected', reason); // onRejected 1
        })
        .catch(reason => {
            console.log('onCatch', reason);
        })

可以看到,上游的reject(1)被then方法中的onRejected()回调函数捕获了,所以不会触发catch方法

如果上游有多个异常错误,则出现第一个错误后后面就不会执行了,会被后面的onRejected()回调函数或者catch()方法捕获到

    new Promise((resolve, reject) => {
        resolve(2)
    })
        .then(value => {
            console.log('onResolved1()', value); // onResolved1() 2
            // 抛出的第一个错误
            throw new Error('onResolved1()异常')
        })
        .then(value => {
            // 此时此处不会走
            console.log('onResolved2()', value);
        }, reason => {
            // 异常被处理了
            console.log('onRejected2()', reason); // onRejected2() Error: onResolved1()异常
            // 又抛出一个异常
            throw new Error('onRejected2()异常')
        })
        .catch(reason => {
            console.log('onCatch', reason); // onCatch Error: onRejected2()异常
        })

3.9 Promise.prototype.finally()

无论如何都会执行的回调函数。无论上游是否成功或者失败,最终都会执行

成功示例

    Promise.resolve(1)
        .then(value => {
            console.log('onResoloved', value); // onResoloved 1
        })
        .catch(reason => {
            console.log('onCatched', reason);
        })
        .finally(() => {
            console.log('finally1执行了'); // finally1执行了
        })

失败示例


    Promise.reject(1)
        .then(value => {
            console.log('onResoloved', value);
        })
        .catch(reason => {
            console.log('onCatched', reason); // onCatched 1
        })
        .finally(() => {
            console.log('finally1执行了'); // finally1执行了
        })

多个finally也都会依次执行

    Promise.resolve(1)
        .then(value => {
            console.log('onResoloved', value); // onResoloved 1
        })
        .catch(reason => {
            console.log('onCatched', reason);
        })
        .finally(() => {
            console.log('finally1执行了'); // finally1执行了
        })
        .finally(() => {
            console.log('finally2执行了'); // finally2执行了
        })

4 promise.then()返回的新promise的结果状态有什么决定?

简单表达:由then()指定的回调函数执行的结果决定

详细表达:

  1. 如果抛出异常,新promise变为rejected,reason为抛出的异常
  2. 如果返回的是非promise的任意值,新promise变为resolved,value为返回的值
  3. 如果返回的是另一个新promise,此promise的结果就会成为新promise的结果

4.1 promise成功后then接收后继续使用then

    new Promise((resolve, reject) => {
        resolve(1)
    })
        .then(value => {
            console.log('onResolved1()', value); // onResolved1() 1
        }, reason => {
            console.log('onRejected1()', reason);
        })
        .then(value => {
            console.log('onResolved2()', value); // onResolved2() undefined
        }, reason => {
            console.log('onRejected2()', reason);
        })
  1. 可以看到,promise中使用resolve(1)改变promise状态,并触发第一个then的onResolved()函数,所以此时打印出onResolved1() 1
  2. 第一个then的onResolved()函数没有返回值,或者说它的返回值为undefined(默认返回值)。根据promise的特性,promise的then方法返回值是一个新的promise对象。暂且隐式命名为promise2,promise2发现没有异常错误,则会把第一个then中onResolved()的返回值resolve出去,即resolve(undefined)
  3. 第二个then接收的是promise2的回调函数,由于上一步promise2resolve(undefined)了,所以会触发then方法的onResolved()回调,由于value是undefined,所以打印结果为onResolved2() undefined

4.2 promise失败后then接收后继续使用then

    new Promise((resolve, reject) => {
        reject(2)
    })
        .then(value => {
            console.log('onResolved1()', value);
        }, reason => {
            console.log('onRejected1()', reason); // onRejected1() 2
        })
        .then(value => {
            console.log('onResolved2()', value); // onResolved2() undefined
        }, reason => {
            console.log('onRejected2()', reason);
        })

原理同上

4.3 promise成功后有返回值,返回值为一个普通值

    new Promise((resolve, reject) => {
        resolve(1)
    })
        .then(value => {
            console.log('onResolved1()', value); // onResolved1() 1
            return 2
        }, reason => {
            console.log('onRejected1()', reason);
        })
        .then(value => {
            console.log('onResolved2()', value); // onResolved2() 2
        }, reason => {
            console.log('onRejected2()', reason);
        })

原理同上

4.4 promise成功后有返回值,返回值是一个promise对象

    new Promise((resolve, reject) => {
        resolve(1)
    })
        .then(value => {
            console.log('onResolved1()', value); // onResolved1() 1
            return Promise.reject(2)
        }, reason => {
            console.log('onRejected1()', reason);
        })
        .then(value => {
            console.log('onResolved2()', value);
        }, reason => {
            console.log('onRejected2()', reason); // onRejected2() 2
        })

如果then中onResolved()和onRejectd()回调函数返回值是一个promise对象,则下一个then函数则会以此promise对象为基础进行回调,就不会以then的返回新的promise对象为基础了。

Promise实战

光说不练只能是纸上谈兵,下面使用promise结合ajax请求实现一个获取接口数据的方法

    /**
    *  请求封装
    */
    function get(url, resolve, reject) {
        let xhr = new XMLHttpRequest();
        xhr.open('GET', url)
        xhr.send()
        xhr.onload = function () {
            if (this.status === 200) {
                resolve(this.response)
            } else {
                reject(this.response)
            }
        }

        xhr.onerror = function () {
            reject(this.status)
        }
    }


    // 使用promise
    new Promise((resolve, reject) => {
        // 获取数据
        get('https://api.apiopen.top/getJoke?page=1&count=2&type=video', resolve, reject)
    })
        .then(value => {
            console.log('获取数据成功', value);
        })
        .catch(reason => {
            console.log('获取数据失败', reason);
        })
请求成功结果

Promise常见面试题汇总

【建议星星】要就来45道Promise面试题一次爽到底(1.1w字用心整理)

你可能感兴趣的:(【ES6】Promise的使用)