JavaScript Promise学习

207 阅读4分钟

1、Promise创建后会立即执行,then或者catch中为异步任务

let promise = new Promise(function (resolve, reject) {
    console.log(111)
    resolve()
})
promise.then(() => {
    console.log(222)
})
console.log(333)

// 111
// 333
// 222

上面代码中,Promise新建后会立即执行,首先打印111。然后then方法指定的回调函数为异步任务,应在同步任务(打印333)执行后再执行,因此打印222最后执行。


2、调用resolvereject并不会终结 Promise 的参数函数的执行

const p = new Promise(function (resolve, reject) {
    resolve(1)
    console.log(2)
})
p.then((value) => {
    console.log(value)
})

// 2
// 1

实例对象p调用resolve(1)后,后面的console.log(2)还是会执行,并且会首先打印,这是因为立即 resolved Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。

一般来说,调用resolvereject之后,Promise的使命就完成了,后继操作应该放在then方法里,而不应该直接写在resolvereject的后面,所以最好在他们前面加上return语句。

const p = new Promise(function (resolve, reject) {
    return resolve(1)
    console.log(2) // 该语句不再执行
})
p.then((val) => {
    console.log(val)
})

// 1


3、resolve函数的参数除了正常的值以外,还可能是另一个Promise实例

const p1 = new Promise(function (resolve, reject) {
    // some code ...
})
const p2 = new Promise(function (resolve, reject) {
    resolve(p1)
})

p2resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作。p1的状态会传递给p2,也就是说,p1的状态决定了p2的状态,如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变,当p1的状态变为resolved或者rejected时,p2的回调函数将会立即执行。

const p1 = new Promise(function (resolve, reject) {
    setTimeout(() => resolve(111), 3000)})
const p2 = new Promise(function (resolve, reject) {
    setTimeout(() => resolve(p1), 1000)
})
p2.then(result => console.log(result))
  .catch(err => console.log(err))

111 // 3秒后打印

上面例子中p1是一个Promise3秒后变为resolved。而p2的状态在1秒之后改变,resolve方法返回的是p1。由于p2也是一个Promise,此时p2自己的状态无效,p2后面的then方法由p1决定,2秒后p1变为resolved触发then方法指定的回调函数。


4、Promise.prototype.then()

Promisethen方法是定义在原型对象Promise.prototype上的,then方法返回的是一个新的Promise实例,不是原来的那个Promise实例,因此可以采用链式写法。

const p = new Promise(function (resolve, reject) {})
p.then(() => {})
 .then(() => {})
 ...


5、Promise.prototype.catch()

Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

const p = new Promise(function (resolve, reject) {
    if (condition) {
        resolve()
    } else {
        reject()
    }
})
p.then((success) => {})
 .catch((err) => {})

实例对象p状态变为resolved,就会调用then方法指定的回调函数,如果变为rejected,则调用catch方法指定的回调函数。另外then方法回调函数如果运行中抛出错误,也会被catch方法捕获。


如果Promise状态已经变为resolved,再抛出错误是无效的,如下代码:

const p = new Promise(function (resolve, reject) {
    resolve('ok')
    throw new Error('fail')
})
p.then(value => console.log(value))
 .catch(error => console.log(error))

ok // Error信息不会被捕获


Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。

const p = new Promise(function (resolve, reject) {})
p.then(() => {})
 .then(() => {})
 .then(() => {})
 .catch((error) => {})
//三个then方法任何一个抛出错误都会被catch捕获


6、Promise.all()

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = new Promise.all([p1, p2, p3])

p的状态由p1p2p3决定,分成两种情况。

(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

如果作为参数的Promise实例,自己定义了catch方法,那么它一旦rejected,并不会触发Promise.all()catch方法。只有参数在rejected状态下未定义catch方法才会触发Promise.all()catch方法。


7、利用Promise判断图片是否加载完成

function loadImg(url) {
    return new Promise(function (resolve, reject) {
        const image = new Image()
        image.onload = function () {
            resolve()
        }
        image.onerror = function () {
            reject()
        }
        image.src = url
    })
}