Promise 学习笔记

128 阅读5分钟

Promise 是什么?

  • promise 是 JS 中进行异步编程的新的解决方案
  • promise 是一个构造函数

为什么要用 Promise?

  • 指定回调函数的方式更加灵活
  • 支持链式调用, 可以解决回调地狱问题

什么是回调地狱?
回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调执行的条件
回调地狱的缺点?
不便于代码阅读, 不便于异常处理
解决方案?
promise 链式调用

Promise 的状态

实例对象的一个属性PromiseState , 有以下三个值:

  • pending 未决定的
  • resolved / fullfilled 成功
  • rejected / 失败

Promise 的状态改变

  1. pending 变为 resolved
  2. pending 变为 rejected

只有这2中情况, 且一个promise对象只能改变一次, 无论变为成功还是失败, 都会只有一个结果数据, 成功的结果数据一般称为 value, 失败的结果数据一般称为 reason.

Promise 对象的值

实例对象中的另一个属性PromiseResult
保存着异步任务(成功/失败)的结果

Promise 的API

1. Promise.then方法: (onResolved, onRejected) => {}

onResolved 函数: 成功的回调函数 (value) => {}
onRejected 函数: 失败的回调函数 (reason) => {}
2. Promise.catch方法: (onRejected) => {} onRejected 函数: 失败的回调函数 (reason) => {} 3. Promise.resolve方法: (value) => {} value: 成功的数据或 promise 对象
说明: 返回一个成功/失败的 promise 对象

// 如果传入的参数为非Promise类型的对象, 则返回的结果为成功promise对象
// 如果传入的参数为 Promise对象, 则参数的结果决定了 resolve 的结果
let p1 = Promise.resolve(123)
let p2 = Promise.resolve(new Promise((resolve, reject) => {
    // resolve('ok')
    reject('error')
}))
console.log(p2)
p2.catch(reason => {
    console.log(reason)
})

4. Promise.reject方法: (value) => {}

返回一个失败的 promise 对象

let p1 = Promise.reject(123)
console.log(p1)
// 无论传入任何类型的参数, 返回的始终是一个失败的 promise 对象

5. Promise.all方法: (promise) => {}

promise: 包含 n 个promise的数组
返回一个新的promise, 只有所有的promise都成功才成功, 只要有一个失败了就直接失败

let p1 = new Promise((resolve, reject) => {
    resolve('ok')
})
let p2 = Promise.resolve(123)
//let p3 = Promise.resolve('success')
let p3 = Promise.reject('error')
const result = Promise.all([p1, p2, p3])
console.log(result)

6. Promise.race方法: (promise) => {}

promise: 包含 n 个 promise 的数组
返回一个新的 promise, 第一个完成的promise的结果状态就是最终的结果状态

let p1 = new Promise((resolve, reject) => {
    // resolve('ok')
    setTimeout(() => {
        resolve('ok')
    }, 1000)
})
let p2 = Promise.resolve(123)
//let p3 = Promise.resolve('success')
let p3 = Promise.reject('error')
const result = Promise.race([p1, p2, p3])
console.log(result)

Promise 的一些关键问题

1. 如何改变promise的状态

  1. resolve(value): 如果当前是pending就会变成resolved
  2. reject(reason): 如果当前是pending就会变成rejected
  3. 抛出异常: 如果当前是pending就会变为rejected
let p1 = new Promise((resolve, reject) => {
    // 1. resolve()
    resolve('ok') // pending => (resolved / fullfilled)
    // 2. reject()
    reject('error') // pending => rejected
    // 抛出异常
    throw '出问题了'
})

2. 一个promise指定多个成功/失败回调函数, 都会调用吗?

当promise改变为对应状态时都会调用

let p1 = new Promise((resolve, reject) => {
    // 下面一行代码代表改变了状态, 如果把它注释掉, 下面两个then方法就不会被调用, 所以才说状态改变就会调用.
    resolve('ok')
})
p1.then(value=> {
    console.log(value)
})
p1.then(value=> {
    alert(value)
})

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

简单的说: 由then()指定的回调函数执行的结果决定
详细的说:

  1. 如果抛出异常, 新promise变为rejected, reason为抛出的异常
  2. 如果返回的时非promise的任意值, 新promise变为resolved, value为返回的值
  3. 如果返回的时一个新的promise, 此 promise的结果就会称为新promise的结果
let p = new Promise((resolve, reject) => {
   resolve('success')
})
let result = p.then(value => {
    //1. 抛出异常
    throw '出来了问题'
    // 2. 返回的结果时非 Promise 类型的对象
    return 123
    // 3,  返回的结果是 Promise 对象
    return new Promise((resolve, reject) => {
        // resolve('ok')
        reject('error')
    })
}, reason => {
    console.warn(reason)
})
console.log(reasult)

4. Promise 如何串联多个操作任务?

  1. promise 的 then() 返回一个新的promise, 可以看成then()的链式调用
  2. 通过then的链式调用串联多个同步/异步任务
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('ok')
    }, 1000)
})
p.then(value => {
    return new Promise((resolve, reject) => {
        resolve('success')
    })
}).then(value => {
    console.log(value)
    // 打印出来的结果是 success
}).then(value => {
    console.log(value)
    // 打印出来的结果 undefined
})

5.Promise 的异常穿透?

  1. 当使用promise的then链式调用时, 可以在最后指定失败的回调
  2. 前面任何操作出了异常, 都会传到最后失败的回调中处理
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        // resolve('ok')
        reject('error')
    }, 1000)
})
p.then(value => {
    console.log('111')
    // throw '错误了'
}).then(value => {
    console.log('222')
}).then(value => {
    console.log('333')
}).catch(reason => {
    console.log(reason)
})
  1. 中断Promise链?
  1. 当使用promise的then链式调用时, 在中间中断, 不再调用后面的回调函数
  2. 方法: 在回调函数中返回一个pending状态的promise对象
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        // resolve('ok')
        reject('error')
    }, 1000)
})
p.then(value => {
    console.log('111')
    // throw '错误了'
    return new Promise(() => {})
}).then(value => {
    console.log('222')
}).then(value => {
    console.log('333')
}).catch(reason => {
    console.log(reason)
})

async 与 await

  • async 函数
  1. 函数的返回值为promise对象
  2. promise对象的结果由async函数执行的返回值决定
async function main() {
    // 1. 返回值是一个非promise类型的数据
    // return 123
    
    // 2. 返回的是一个promise对象
    return new Promise((resolve, reject) => {
        // resolve('ok')
        reject('error')
    })
    // 抛出异常
    throw '错误了'
}
let result - main()
console.log(result)
  • await 函数
  1. await 右侧的表达式一般为promise对象, 但也可以是其他的值
  2. 如果表达式是promise对象, await 返回的是 promise成功的值
  3. 如果表达式是其他值, 直接将此值作为await的返回值

注意

  1. await 必须写在 async 函数中, 但 async 函数中可以没有 await
  2. 如果 await 的 promise 失败了, 就会抛出异常, 需要通过 try...catch捕获处理
async function fn() {
    let p = new Promise((resolve, reject) => {
        // resolve('ok')
        reject('error')
    })
    // 1. 右侧是promise的情况
    // let res = await p;
    // 2. 右侧是其他类型的数据
    // let res = await 20
    // 3. 如果 promise 是失败的状态
    try {
        let res = await p
    } catch(e){
        console.log(e)
    }
    console.log(res)
}