JS之Promise

50 阅读3分钟

1.什么是Promise

(1)Promise是异步编程的一种解决方案,它是一个对象,可以获得异步操作的消息,避免了回调地狱。
(2)Promise是一个构造函数,接收一个函数作为参数,返回一个Promise,这使它能够支持链式调用。一个Promise实例存在三种状态:Pending(进行中)fulfilled(已完成)rejected(已拒绝),Promise的状态只能由pending -> fulfilled 或者 pendding -> reject,且状态一经改变,任何其他操作都无法改变这个状态。
(3)Promise身上有all、race、reject、resolve这几个方法,它的原型上有then、catch、finally等方法,为两个状态的改变注册回调函数,属于微任务

但是Promise也存在缺点:

  1. 无法取消Promise,一旦新建它就会立即执行,无法中途取消;
  2. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部;
  3. 当处于Pending状态时,无法得知目前进展到了哪个阶段(刚开始?还是即将完成?)

2.基本API

2.1 Promise.resolve()

Promise.resolve(value)方法可以将状态pending 改为 fulfilled,带着给定值解析过的Promise对象,如果参数本身就是一个Promise对象,则直接返回这个Promise对象
then()方法返回一个Promise,最多有两个参数,成功的回调函数以及失败的回调函数
语法: p.then(value => { // fulfillment }, reason => { // rejection });

function runAsync() {
    var p = new Promise((resolve, reject) => {
        //模拟异步操作
        setTimeout(function () {
            console.log('1秒后执行')
            resolve('成功')
        }, 1000)
    })
    return p
}
runAsync().then(function (data) {
    console.log(data)
})

输出结果:
1秒后执行
成功

2.2 Promise.reject()

Promise.reject() 方法返回一个带有拒绝原因的Promise对象,并将状态由pendding改为rejected

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

2.3 Promise.prototype.catch()

与then方法的第二个参数一样,用来指定reject的回调,返回一个promise

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

输出结果:
4 或者 失败 数字太大了

2.4 Promise.all()

用于将多个Promise实例包装成一个新的promise,这个方法接收一个数组作为参数,它的状态由数组内的promise实例决定。
当数组内的promise状态都变为fulfilled,最终的状态才会变为fulfilled,这个Promise的resolve回调执行是在所有输入的promise的resolve回调都结束,或者输入的iterable里没有promise了的时候。它的reject回调执行是,只要任何一个输入的promise的reject回调执行或者输入不合法的promise就会立即抛出错误,并且reject的是第一个抛出的错误信息。

注:多个promise是同时、并行执行的,而不是按顺序执行;如果其中一个请求失败,另外两个请求依旧会执行,promise不会被中断,只是最终不会采纳另外两个请求的结果

function runAsync() {
    var p = new Promise((resolve, reject) => {
        setTimeout(function () {
            console.log('1秒后执行')
            resolve('成功')
        }, 1000)
    })
    return p
}
function runAsync1() {
    var p = new Promise((resolve, reject) => {
        setTimeout(function () {
            console.log('2秒后执行')
            resolve('成功')
        }, 2000)
    })
    return p
}
function runAsync2() {
    var p = new Promise((resolve, reject) => {
        setTimeout(function () {
            console.log('3秒后执行')
            resolve('成功')
        }, 3000)
    })
    return p
}

Promise.all([runAsync(),runAsync1(), runAsync2()]).then(function (res) {
    console.log(res)
}).catch(function (reason) {
    console.log(reason)
})

输出结果:
1秒后执行
2秒后执行
3秒后执行
[ '成功1', '成功2', '成功3' ]
function runAsync() {
    var p = new Promise((resolve, reject) => {
        setTimeout(function () {
            console.log('1秒后执行')
            resolve('成功1')
        }, 1000)
    })
    return p
}
function runAsync1() {
    var p = new Promise((resolve, reject) => {
        setTimeout(function () {
            console.log('2秒后执行')
            reject('失败')
        }, 2000)
    })
    return p
}
function runAsync2() {
    var p = new Promise((resolve, reject) => {
        setTimeout(function () {
            console.log('3秒后执行')
            resolve('成功3')
        }, 3000)
    })
    return p
}

Promise.all([runAsync(),runAsync1(), runAsync2()]).then(function (res) {
    console.log(res)
}).catch(function (reason) {
    console.log(reason)
})

输出结果:
1秒后执行
2秒后执行
失败
3秒后执行

2.5 Promise.race()

该方法同样也是将多个promise包装成一个promise实例。该方法接收一个数组作为参数,当参数中有一个promise实例的状态发生改变(resolve或reject),返回的Promise的状态也跟着改变,并把第一个改变状态的promise的返回值作为它的值。在第一个promise对象变为resolve后,并不会取消其他promise对象的执行

function runAsync() {
    var p = new Promise((resolve, reject) => {
        setTimeout(function () {
            console.log('1秒后执行')
            resolve('成功1')
        }, 2000)
    })
    return p
}
function runAsync1() {
    var p = new Promise((resolve, reject) => {
        setTimeout(function () {
            console.log('2秒后执行')
            resolve('成功2')
        }, 1000)
    })
    return p
}
function runAsync2() {
    var p = new Promise((resolve, reject) => {
        setTimeout(function () {
            console.log('3秒后执行')
            resolve('成功3')
        }, 3000)
    })
    return p
}
Promise.race([runAsync(),runAsync1(), runAsync2()]).then(function (res) {
    console.log(res)
})

输出结果:
2秒后执行
成功2
1秒后执行
3秒后执行

2.6 Promise.prototype.finally()

该方法返回一个Promise,在promise结束时,无论结果是fulfilled还是rejected,都会执行指定的回调函数

3. 基本API代码实现

3.1 Promise.all()简单实现

Promise.All = function (arr) {
    const res = []
    //记录状态为resolve的promise的数量
    let index = 0
    return new Promise((resolve, reject) => {
        arr.forEach((p,i) => {
            Promise.resolve(p).then(value => {
                index++
                res[i] = value
                if (index === arr.length) {
                    resolve(res)
                }
            },err => {
                reject(err)
            })
        })
    })
}

3.2 Promise.race()简单实现

Promise.myRace = function (promiseArr) {
    return new Promise((resolve, reject) => {
        promiseArr.forEach(p => {
            Promise.resolve(p).then(value => {
                resolve(value)
            }, err => {
                reject(err)
            })
        })
    })
}