携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
一、Promise是什么
Promise是异步操作的一种解决方案,在Promise出现之前,主要采用回调函数来处理异步问题。
- 但是层层嵌套的回调函数会形成 “回调地狱”
- 此时Promise出现了,可以更加优雅的来处理异步操作,从而解决了“回调地狱” 的问题
那么什么是回调地狱呢?
我们来看这样一个例子,比如实现一个“顺时针”运动的正方形:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>运动的小球</title>
<style>
* {
padding: 0;
margin: 0;
}
#box {
width: 300px;
height: 300px;
background-color: red;
transition: all 0.5s;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
// 运动
const move = (el, { x = 0, y = 0 } = {}, end = () => { }) => {
el.style.transform = `translate3d(${x}px, ${y}px, 0)`;
// transitionend 时执行传入的end方法
el.addEventListener(
'transitionend',
() => {
end();
},
false
);
};
const boxEl = document.getElementById('box');
document.addEventListener(
'click',
() => {
move(boxEl, { x: 150 }, () => {
move(boxEl, { x: 150, y: 150 }, () => {
move(boxEl, { y: 150 }, () => {
move(boxEl, { x: 0, y: 0 });
});
});
});
},
false
);
</script>
</body>
</html>
可见,通过回调函数的方式来实现会出现这种层层嵌套的问题从而导致了“回调地狱”,代码逻辑多的话会更加复杂,如果需求一旦变更,改起来也会很麻烦。
二、Promise三种状态
Promise是一个构造函数,在使用Promise时我们需要实例化一个Promise对象然后去使用。
pending状态
Promise的构造函数中接收一个回调函数来作为参数
const p = new Promise(() => {});
回调函数中接收两个参数,这两个参数是两个函数,我们通常用 resolve 与 reject 来代表这两个函数
- resolve 作为执行成功时的回调
- reject 作为执行失败时的回调
const p = new Promise((resolve, reject) => {})
console.log(p)
此时打印出Promise实例,可以发现当什么都不做时,此时状态为 pending(表示等待中:未完成)
fulfilled状态
当在回调函数中调用resolve时:
const p = new Promise((resolve, reject) => {
resolve()
})
console.log(p)
可以发现此时状态变为了 fulfilled(代表执行成功)
rejected状态
当在回调函数中调用reject时:
const p = new Promise((resolve, reject) => {
reject()
})
console.log(p)
可以发现此时状态变味了 rejected(代表执行失败)
可以发现Promise实例有三种状态:
- pending
- fulfilled
- rejected
需要注意的是,Promise的状态一旦完成了变化就不会再次被改变了!
- 如果先调用了resolve,又调用reject,那么状态依旧是fulfilled
- 如果先调用了reject,又调用了resolve,那么状态依旧是rejected
const p1 = new Promise((resolve, reject) => {
resolve()
reject()
})
console.log(p1) // [[PromiseState]]: "fulfilled"
const p2 = new Promise((resolve, reject) => {
reject()
resolve()
})
console.log(p2) // [[PromiseState]]: "rejected"
三、Promise的实例方法
then方法
then方法的基本使用
then方法中接收两个回调函数
- 第一个回调函数会在调用resolve时执行
- 第二个回调函数会在调用reject时执行
也就是说:当Promise实例从pending 变成 fulfilled 时,会执行 then 中的第一个回调函数;从 pending 变成 rejected 时,会执行then中的第二个回调函数
const p1 = new Promise((resolve, reject) => {
resolve()
})
p1.then(() => {
console.log('p1 success')
}, () => {
console.log('p1 error')
}) // p1 success
const p2 = new Promise((resolve, reject) => {
reject()
})
p2.then(() => {
console.log('p2 success')
}, () => {
console.log('p2 error')
}) // p2 error
resolve与reject中可以写入要传递的数据
- resolve中传递的数据可以被then中第一个回调函数接收
- reject中传递的数据可以被then中第二个回调函数接收
resolve与reject中可以传递字符串、对象等数据,我们一般会在reject中写入一个Error对象
const p1 = new Promise((resolve, reject) => {
// resolve('success') // 可以传递字符串
resolve({ name: 'zs' }) // 也可以传递对象
})
p1.then((res) => {
console.log(res) // { name: 'zs' }
}, () => {
console.log('p1 error')
})
const p2 = new Promise((resolve, reject) => {
// reject('err')
// reject({ name: 'zs'})
reject(new Error('err...'))
})
p2.then(() => {
console.log('p2 success')
}, (err) => {
console.log(err)
}) // Error: err...
then方法的链式调用
then方法执行后返回一个新的Promise对象
const p1 = new Promise((resolve, reject) => {
resolve('success')
})
const p2 = p1.then((res) => {
console.log(res) // success
}, () => {
console.log('p1 error')
})
console.log(p1 === p2) // false 返回的是一个新的Promise对象
由于then方法可以返回一个新的Promise对象,所以我们可以进行链式调用
const p1 = new Promise((resolve, reject) => {
resolve('success')
})
p1.then((res) => {
console.log(res)
}, () => {
console.log('p1 error')
}).then().then().then()
那么链式调用的then函数会执行其中的哪个回调呢?
const p = new Promise((resolve, reject) => {
resolve()
})
p.then((res) => {
console.log('success 1')
}, () => {
console.log('error 1')
}).then(
(res) => {
console.log('success 2')
},
(err) => {
console.log('error 2')
}
)
// success1 success2
可见,分别打印出success1 与 success2,第一次输出success 1是由于调取了resolve,那么第二次输出success 2是否也与调取resolve有关呢?
const p = new Promise((resolve, reject) => {
reject()
})
p.then((res) => {
console.log('success 1')
}, () => {
console.log('error 1')
}).then(
(res) => {
console.log('success 2')
},
(err) => {
console.log('error 2')
}
) // error 1 success 2
但是在调用reject之后第二次依旧输出的是success 2,这是因为什么呢?
then方法返回Promise对象的状态改变
then方法中回调的执行由上一个返回的promise对象的状态来决定
在then方法中,return后面的数据在内部会用Promise包装一下
先来看如下代码:
const p = new Promise((resolve, reject) => {
resolve()
})
p.then((res) => {
console.log('success 1')
}, () => {
console.log('error 1')
}).then(res => {
console.log('success 2', res)
}) // success 2 undefined
在以上代码中,由于执行了resolve,那么首先会执行then方法中会执行第一个回调函数,由于在该回调中没有return,那么就相当于return undefined。
由于return的内容会被Promise包装一下,而且默认会执行resolve来将内容传递出去,所以以上代码等同于于:
const p = new Promise((resolve, reject) => {
resolve()
})
p.then((res) => {
console.log('success 1')
// return undefined
// 相当于
return new Promise(resolve => {
resolve(undefined)
})
}, () => {
console.log('error 1')
}).then(res => {
console.log('success 2', res)
}) // success 2 undefined
这也就是为什么当链式调用then时,会执行第一个回调函数而且接收到的数据为undefined了。
所以我们在第一个then方法中的回调函数中return 其他内容时也会在第二个then方法的回调函数中接收到:
由于then回调方法中return的内容会被Promise包装后默认执行其中的resolve方法,所以生成的新的Promise实例是fulfilled状态
- 那么如果想在then的回调方法中返回rejected状态的promise对象,就需要自己手动return一个执行reject方法的Promise对象
const p = new Promise((resolve, reject) => {
resolve()
})
p.then((res) => {
console.log('success 1')
return new Promise((resolve, reject) => {
reject(new Error('err...'))
})
}, () => {
console.log('error 1')
}).then((res) => {
console.log('success 2', res)
}, (err) => {
console.log('error 2', err)
}) // success 1 error 2 Error: err...
所以then方法中执行哪个回调是与上一个then方法中返回的Promise对象的状态有关的,而与上一个then方法执行哪个回调函数没有关系,即使上一个then方法中执行了失败时的回调,也不代表下一个then方法会执行失败时的回调,因为有可能在上一个then方法的失败回调中返回了一个成功状态的Promise对象
接下来,趁热打铁,练习一下:
const p = new Promise((resolve, reject) => {
reject(1)
})
p.then(res => {
console.log(res)
}, err => {
console.log(err)
}).then(() => {
console.log("a")
return 2
}, () => {
console.log("b")
return 3
}).then(res => {
console.log(res)
}, err => {
console.log(err)
})
- 以上代码中,p是一个失败状态的Promise对象,所以在第一个then方法中会执行失败时的回调,输出1
- 由于第一个then方法的失败回调中没有return内容,相当于return undefined,默认会返回一个成功状态的Promise对象,所以第二个then方法中会执行成功时的回调,输出a
- 第二个then方法中的成功回调中return 2,相当于返回了一个成功状态的Promise对象,并resolve(2)
- 所以在第三个then方法中会执行成功时的回调,输出 2
- 所以最终结果为 1 a 2
使用then方法来解决回调地狱的问题
掌握了then方法的基本使用方法后,可以使用then方法来解决正方形顺时针运动的回调地狱问题了:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>then()</title>
<style>
* {
padding: 0;
margin: 0;
}
#box {
width: 300px;
height: 300px;
background-color: red;
transition: all 0.5s;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
// 运动
const move = (el, { x = 0, y = 0 } = {}, end = () => { }) => {
el.style.transform = `translate3d(${x}px, ${y}px, 0)`;
el.addEventListener(
'transitionend',
() => {
end();
},
false
);
};
const boxEl = document.getElementById('box');
const movePromise = (el, point) => {
return new Promise(resolve => {
move(el, point, () => {
resolve();
});
});
};
document.addEventListener(
'click',
() => {
movePromise(boxEl, { x: 150 })
.then(() => {
return movePromise(boxEl, { x: 150, y: 150 });
})
.then(() => {
return movePromise(boxEl, { x: 0, y: 150 });
})
.then(() => {
return movePromise(boxEl, { y: 0 });
});
},
false
);
</script>
</body>
</html>
catch方法
在使用then方法时,大多情况下只处理成功状态的Promise对象。此时为了更好的语义化,可以使用catch方法来专门处理失败状态的Promise对象。
- 实际上 catch本质上是then方法的特例,相当于then(null, err => {})
new Promise((resolve, reject) => {
reject('err')
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
}) // err
以上代码相当于:
当错误没有被捕获是会向下传递的,由于第一个then方法中没有失败时的回调所以没有捕获,那么会向下传递,被catch所捕获(也就是then(null, err => {}))
由于catch方法是then方法的特例,所以同样会返回一个新的Promise对象,这样一来,catch方法后面也可以链式调用then方法
new Promise((resolve, reject) => {
reject('err')
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
}).then(res => {
console.log(res) // undefined
})
在以上代码中,catch的回调里没有return内容,相当于return undefined,也就相当于返回了一个成功状态的Promise对象(resolve(undefined)), 所以最后会输出undefined
如果catch方法中的内容想被后面的catch所捕获:
- 可以在该catch方法中直接throw抛出异常
- 也可以手动返回一个失败状态的Promise对象
new Promise((resolve, reject) => {
reject('err')
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
throw new Error('err...')
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err) // Error: err...
})
// 或者
new Promise((resolve, reject) => {
reject('err')
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
return new Promise((resolve, reject) => {
reject(new Error('err...'))
})
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err) // Error: err...
})
finally方法
无论调取了resolve还是reject,finally方法最终都会执行
- finally方法中接收不到任何数据
- 所以finally方法一般是用来处理最终状态的,比如在获取后端数据的时候会加一个loading状态,但是不管最后获取成功了还是失败了都会关闭loading状态,这种情况就可以放在finally方法中处理
new Promise((resolve, reject) => {
resolve('success')
}).then(res => {
console.log(res)
}).finally(() => {
console.log('111')
}) // success 111
new Promise((resolve, reject) => {
reject('err')
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
}).finally(() => {
console.log('222')
}) // err 222
要注意的是,finally中是拿不到数据的
一般用finally来处理最终状态,比如在获取数据后将 loading 置为false
let loading = true
new Promise((resolve, reject) => {
resolve('success')
}).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
}).finally(() => {
loading = false
})
四、Promise的静态方法
Promise.resolve()
基本用法
Promise.resolve()是成功状态Promise的一种简写形式
new Promise(resolve => resolve('success'))
// 相当于
Promise.resolve('success')
可以接收的参数
Promise.resolve('success').then(res => {
console.log(res)
}) // success
Promise.resolve()中除了可以接收字符串、对象...这种普通参数外,也可以接收一个Promise对象
const p1 = new Promise((resolve) => {
setTimeout(resolve, 1000, '我执行了')
// 相当于:
// setTimeout(() => {
// resolve('我执行了')
// }, 1000)
})
Promise.resolve(p1).then(res => {
console.log(res)
}) // 1秒后输出:我执行了
以上代码中,在1秒钟后会输出"我执行了",实际上是因为:
当Promise.resolve()中接收一个Promise对象时,什么都不会做,直接将该对象返回
所以以上代码相当于:
所以Promise.resolve()中一旦接收的是一个Promise对象时,那么会由该Promise对象的状态来决定执行哪一个回调
const p1 = new Promise((resolve, reject) => {
reject('err')
})
// 传入的Promise对象状态为rejected,所以会被catch捕获
Promise.resolve(p1).then(res => {
console.log(res)
}).catch(err => {
console.log(err) // err
})
Promise.resolve中也可以接收具有then方法的对象
- 当传入then方法的对象时,这个then方法将会直接被执行
- 该then方法中接收resolve与reject回调
- 在该then方法中执行的resolve、reject将会决定返回的新的Promise的状态
const thenable1 = {
then(resolve, reject) {
console.log('1')
resolve('a')
}
}
// 接收的thenable1对象中的then方法中执行了resolve,相当于返回了一个成功状态的Promise对象
// 所以会执行.then方法中的回调
Promise.resolve(thenable1).then(res => {
console.log('success...', res)
}).catch(err => {
console.log('err...', err)
}) // 1 success...a
const thenable2 = {
then(resolve, reject) {
console.log('2')
reject('b')
}
}
// 接收的thenabl2对象中的then方法中执行了reject,相当于返回了一个失败状态的Promise对象
// 所以会执行.catch方法中的回调
Promise.resolve(thenable2).then(res => {
console.log('success...', res)
}).catch(err => {
console.log('err...', err)
}) // 2 err...b
Promise.reject()
基本用法
Promise.reject()是失败状态Promise的一种简写形式
new Promise((resolve, reject) => {
reject('err')
})
// 相当于
Promise.reject('err')
所以,当我们想要在then方法中返回一个失败状态的Promise对象时,可以通过Promise.reject()来简写
new Promise(resolve => {
resolve('123')
}).then(res => {
return Promise.reject('err')
}).catch(err => {
console.log(err) // err
})
可以接收的参数
Promise.reject()中不管接收什么参数,都会原封不动地向后传递,作为后续方法的参数
const p1 = new Promise(resolve => {
setTimeout(() => {
resolve('success')
}, 1000)
})
// 此时不会过1秒后打印success, 而是直接将该Promise对象传递给catch
Promise.reject(p1).catch(err => {
console.log(err)
})
1秒后:
Promise.all()
基本用法
Promise.all()用于关注多个Promise对象的状态变化
- 接收一个包含多个Promise对象的数组,包装成一个新的Promise实例来返回
- Promise.all返回的新的实例的then方法会在传递的所有Promise实例都执行完成时才执行(前提是所有的Promise对象都变成了fulfilled状态)
- 返回的新实例中then方法中接收到的数据为数组
1)当传递的所有Promise实例的状态均为fulfilled时,才会执行then中成功的回调:
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
const p1 = delay(1000).then(() => {
console.log('p1')
return 'p1'
})
const p2 = delay(2000).then(() => {
console.log('p2')
return 'p2'
})
const p = Promise.all([p1, p2])
p.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
// 打印结果:
// p1
// p2
// ['p1', 'p2']
一旦有一个Promise实例状态变成了rejected,那么就会执行失败的回调
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
const p1 = delay(1000).then(() => {
console.log('p1')
return 'p1'
})
const p2 = delay(2000).then(() => {
console.log('p2')
return Promise.reject('err')
})
const p = Promise.all([p1, p2])
p.then(res => {
console.log(res)
}).catch(err => {
console.log(err) // err
})
// 打印结果:
// p1
// p2
// err
注意事项
一旦其中有执行失败的Promise对象,那么all方法返回的Promise实例将会立即执行失败时的回调,即使还有未执行完成的Promise实例
- 这是因为一旦有一个执行失败的Promise对象就会确定最终要执行失败时的回调了
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
const p1 = delay(1000).then(() => {
console.log('p1')
return Promise.reject('err')
})
const p2 = delay(2000).then(() => {
console.log('p2')
return 'p2'
})
const p = Promise.all([p1, p2])
p.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
// 打印结果:
// p1
// err
// p2
可见,以上代码中,先输出了err最后才打印出了p2
Promise.race()
Promise.race()也是用于关注多个Promise对象的状态变化
- 接收一个包含多个Promise对象的数组,包装成一个新的Promise实例来返回
- 与Promise.all()不同的是,返回的新的Promise对象的状态由第一个执行完成的Promise实例来决定
基本用法
由于Promise.race()返回的Promise实例由传入的最先执行完成的Promise实例来决定,所以:
- 如果最先执行完成的Promise实例状态为fulfilled,那么生成的新的Promise实例会执行then方法中成功时的回调
- 如果最先执行完成的Promise实例状态为rejected,那么生成的新的Promise实例会执行then方法中失败时的回调
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
const p1 = delay(1000).then(() => {
console.log('p1完成了')
return 'p1'
})
const p2 = delay(2000).then(() => {
console.log('p2完成了')
return Promise.reject('p2')
})
const p = Promise.race([p1, p2])
p.then(res => {
console.log('success...', res) // success... p1
}).catch(err => {
console.log('err..', err)
})
// 打印结果:
// p1完成了
// success... p1
// p2完成了
所以,如果p1中then方法中返回一个失败状态的promise对象,则将会被catch捕获:
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
const p1 = delay(1000).then(() => {
console.log('p1完成了')
return Promise.reject('p1')
})
const p2 = delay(2000).then(() => {
console.log('p2完成了')
return 'p2'
})
const p = Promise.race([p1, p2])
p.then(res => {
console.log('success...', res)
}).catch(err => {
console.log('err..', err) // err... p1
})
// 打印结果:
// p1完成了
// err... p1
// p2完成了
Promise.allSettled()
Promise.allSettled()也是用于关注多个Promise对象的状态变化
- 接收一个包含多个Promise对象的数组,包装成一个新的Promise实例来返回
- 返回的新的Promise的实例的then方法中的回调会在所有传入的Promise实例执行完成后执行
- 无论其中有没有执行失败的Promise实例,最终均会执行then中成功的回调
- 该回调中接收到的数据以数组的形式呈现
基本用法
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
const p1 = delay(1000).then(() => {
console.log('p1完成了')
return Promise.reject('p1')
})
const p2 = delay(2000).then(() => {
console.log('p2完成了')
return 'p2'
})
const p = Promise.allSettled([p1, p2])
p.then(res => {
console.log('success...', res) // success... [{status: 'rejected', reason: 'p1'}, {status: 'fulfilled', value: 'p2'}]
}).catch(err => {
console.log('err..', err)
})
// 打印结果:
// p1完成了
// p2完成了
// success... [{status: 'rejected', reason: 'p1'}, {status: 'fulfilled', value: 'p2'}]
最终then方法中回调函数接收到的数据为一个数组,其中包含了一个个对象
执行成功的Promise返回的结果用: { status: 'fulfilled', value: '返回的数据' } 表示;执行失败的Promise返回的结果用: { status: 'rejected', reason: '返回的数据' } 表示
Promise.any()
Promise.any()也是用于关注多个Promise对象的状态变化
- 接收一个包含多个Promise对象的数组,包装成一个新的Promise实例来返回
- 接收的所有Promise对象都变成rejected时,返回的Promise实例才会变成rejected状态
- 只要其中有一个Promise对象变成了fulfilled,那么返回的Promise实例就是fulfilled状态
基本用法
其中有一个Promise实例执行成功则返回Promise实例对象的状态就是fullfiled
// 失败
const p1 = new Promise((resolve, reject) => {
reject()
})
// 成功
const p2 = new Promise((resolve, reject) => {
resolve()
})
const p = Promise.any([p1, p2])
console.log(p) // [[PromiseState]]: "fulfilled"
传入的所有Promise实例均执行失败,返回的Promise实例状态才是rejected
// 失败
const p1 = new Promise((resolve, reject) => {
reject()
})
// 失败
const p2 = new Promise((resolve, reject) => {
reject()
})
const p = Promise.any([p1, p2])
console.log(p) // [[PromiseState]]: "rejected"
注意事项
Promise.any()不会因为某个Promise实例变成失败状态而结束
- 该方法用于返回第一个执行成功的Promise实例
- 只要有一个Promise实例执行成功了则会终止,不会等待其他Promise实例全部执行完成
const p1 = new Promise((resolve, reject) => {
reject('err')
})
const p2 = new Promise((resolve) => {
setTimeout(() => {
resolve('p2 success')
}, 1000)
})
const p3 = new Promise((resolve) => {
setTimeout(() => {
resolve('p3 success')
}, 2000)
})
const p = Promise.any([p1, p2, p3])
p.then(res => {
console.log(res) // p2 success
})
由于p1是失败状态,所以Promise.any()并没有结束,而是返回了第一个执行成功的Promise实例,即p2,所以最后会输出 p2 success 。此时会终止执行,所以不会打印p3 success
与Promise.all()和Promise.race()的区别
1)Promise.all() 返回的是一组值,而Promise.any()只得到一个成功值(假设至少有一个Promise执行成功的话),所以当我们只需要一个Promise执行成功而不关心是哪一个时,Promise.any()比较有用
2)Promise.race()总是返回第一个结果值,不管是否执行成功;而Promise.any()返回的是第一个成功的值,它会忽略掉所有被拒绝的Promise,直到有一个Promise成功。
实际应用场景
实际开发中,可能会遇到这种场景:一次性加载多张图片,哪一张先加载出来就显示哪一张,那么此时就可以使用Promise.any()来实现。
五、Promise的注意事项
关于resolve或reject执行后的代码
如果没有return,在调用resolve / reject后,如果后面还写了代码逻辑,依旧会执行
- 但是理论上在调用resolve / reject后,变更了Promise的状态代表执行完毕了,所以不推荐在调用resolve / reject后再去编写代码逻辑
- 如果后续还是需要逻辑的处理推荐放在then 或catch中
- 为了避免这种情况的发生,可以在调用resolve / reject前加上return
new Promise((resolve) => {
resolve('success')
console.log('a')
}) // a
new Promise((resolve, reject) => {
reject('err')
console.log('b')
}) // b
以上写法不推荐,推荐将后续代码逻辑放在then / catch中:
new Promise((resolve) => {
resolve('success')
}).then(res => {
console.log('a')
})
new Promise((resolve, reject) => {
reject('err')
}).catch(err => {
console.log('b')
})
所以为了避免在执行resolve / reject后加入其他代码逻辑,可以在调取resolve / reject 前加上return :
new Promise((resolve) => {
return resolve('success')
}).then(res => {
console.log('a')
})
new Promise((resolve, reject) => {
return reject('err')
}).catch(err => {
console.log('b')
})
Promise.all / race / allSettled / any的 参数问题
1)这些方法都是接收一个Promise对象的数组,但如果传递的数组的对象不是Promise对象,那么会在内部将其转成Promise对象
拿Promise.all举例:
Promise.all([1, 2, 3]).then(datas => {
console.log(datas)
})
// 等价于
Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
]).then(datas => {
console.log(datas)
})
2)不只是数组,任何可迭代的对象均可作为参数传入到这些方法中
如:数组、字符串、Set、Map、NodeList、arguments......
Promise.all(new Set([1, 2, 3])).then(datas => {
console.log(datas)
})
Promise.all / race / allSettled / any的 错误处理
如果传递的多个Promise有执行失败的,那么错误既可以通过本身的Promise实例去处理,也可以通过生成的新的Promise实例去统一处理
- 需要注意的是,如果自身的Promise实例已经处理过了,那么将不会向下传递,所以生成的Promise实例就不会再次去处理该错误了
通过自身的Promise实例去处理:
// 失败,通过自身的Promise实例去处理
const p1 = new Promise((resolve, reject) => {
reject('err1')
}).catch(err => {
console.log('捕获到了错误', err) // 捕获到了错误err1
})
const p2 = new Promise((resolve, reject) => {
resolve()
})
const p = Promise.all([p1, p2]).catch(err => {
console.log(err) // 不再会处理
})
通过新的Promise实例统一处理:
const p1 = new Promise((resolve, reject) => {
reject('err')
})
const p2 = new Promise((resolve, reject) => {
resolve()
})
// 统一处理
Promise.all([p1, p2]).catch(err => {
console.log(err) // err
})
六、Promise的应用
异步加载图片
为了提高首屏渲染速度,我们一般会将后续才会看到的图片采用异步加载的方式来实现,不会直接去加载,比如在浏览第一屏数据的时候,会利用这个时间去再去加载第二屏的图片数据,这样既能保证首屏加载速度又能在用户看到第二屏数据的时候图片立马就能呈现出来提升用户体验。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Promise 的应用</title>
<style>
#img {
width: 80%;
padding: 10%;
}
</style>
</head>
<body>
<img
src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2Ftp09%2F21031FKU44S6-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1662274017&t=e57bda426e67a8f417b3e13872b15ab0"
alt="" id="img" />
<script>
// 异步加载图片
const loadImgAsync = url => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
resolve(img);
};
img.onerror = () => {
reject(new Error(`Could not load image at ${url}`));
};
img.src = url;
});
};
const imgDOM = document.getElementById('img');
loadImgAsync('https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F1113%2F041620104229%2F200416104229-8-1200.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1662274017&t=e8d99f7a7c720d6c01620a63c2dee80f')
.then(img => {
console.log(img.src);
setTimeout(() => {
imgDOM.src = img.src;
}, 1000);
})
.catch(err => {
console.log(err);
});
</script>
</body>
</html>