1、Promise遵循规则
- Promise 是一个提供符合标准的 then () 方法的对象。
- 初始状态是 pending,能够转换成 fulfilled 或 rejected 状态。
- 一旦 fulfilled 或 rejected 状态确定,再也不能转换成其他状态。
- 一旦状态确定,必须要返回一个值,并且这个值是不可修改的。
2、解决问题
由于 JavaScript 是单线程事件驱动的编程语言,通过回调函数管理多个任务。开发中,因为回调函数的滥用,很容易产生被人所诟病的回调地狱问题。Promise 的异步编程解决方案比回调函数更加合理,可读性更强。
function callback() {
return function() {
request(url1, function() {
request(url2, function() {
request(url3, function() {
request(url4, function() {
request(url5, function() {
request(url6, function() {
})
})
})
})
})
})
}
}
现实业务场景:
function test1() {
getCode({
userId,
success: code => {
getList({
code,
success: data => {
renderList({
list: data.list,
success: () => {
uploadLog()
},
fail: err => {
console.error(err)
}
})
},
fail: err => {
console.error(err)
}
})
},
fail: err => {
console.error(err)
}
})
}
使用Promise后:
function test2() {
getCode({
userId,
}).then(() => {
return getList()
}).then(() => {
return renderList()
}).then(() => {
return uploadLog()
}).catch((err) => {
console.error(err)
})
}
3、实现Promse
Promise 的运转实际上是一个观察者模式,then() 中的匿名函数充当观察者,Promise 实例充当被观察者。
// 实现
function promise() {
let pending = [] // 收集观察者
let value = undefined
return {
resolve: (_value) => {
value = _value
if (pending) {
pending.forEach(callback => callback(value))
pending = undefined
}
},
then: (callback) => {
if (pending) {
pending.push(callback)
} else {
callback(value)
}
}
}
}
// 测试
function testPromise() {
let p = promise()
setTimeout(() => {
p.resolve('resolve!')
}, 5000)
return p
}
testPromise().then(res => {
console.log(res)
})
console.log('endend')
Promise 出现后使用 then() 接收事件的状态,且只会接收一次。
const p = new Promise(r => r(1))
.then(res => {
console.log(res) // 1
return Promise.resolve(2)
.then(res => res + 10) // === new Promise(r => r(1))
.then(res => res + 10) //
})
.then(res => {
console.log(res) // 22
return 3 // === Promise.resolve(3)
})
.then(res => {
console.log(res) // 3
})
.then(res => {
console.log(res) // undefined
return 'meme'
})
p.then(console.log.bind(null, '输出最后end'))
由于返回一个 Promise 结构体永远返回的是链式调用的最后一个 then(),所以在处理封装好的 Promise 接口时没必要在外面再包一层 Promise。
// 包一层 Promise
function api1() {
return new Promise((resolve, reject) => {
axios.get(url).then(data => {
// ...
resolve(data)
})
})
}
// better
function api2() {
return axios.get(url).then(data => {
// ...
return data
})
}
4、管理多个 Promise
Promise.all() / Promise.race() 可以将多个 Promise 实例包装成一个 Promise 实例,在处理并行的、没有依赖关系的请求时,能够节约大量的时间。
function wait(ms) {
return new Promise(resolve => setTimeout(resolve.bind(null, ms), ms))
}
Promise.all([wait(2000), wait(4000), wait(3000)])
.then(console.log)
Promise.race([wait(2000), wait(4000), wait(3000)])
.then(console.log)
5、async&await 用法
async&await 实际上只是建立在 Promise 之上的语法糖,让异步代码看上去更像同步代码,所以 async&await 在 JavaScript 线程中是非阻塞的,但在当前函数作用域内具备阻塞性质。
let ok = null
async function test() {
console.log(1111)
console.log(await new Promise(resolve => ok = resolve))
console.log(3333)
}
test()
ok(2222)
使用 async&await 的优势
写更少的代码,不需要特地创建一个匿名函数,放入 then() 方法中等待一个响应。
function getInfo() {
return getData().then(
data => {
return data
}
)
}
async function getInfo() {
return await getData()
}
处理条件语句
当一个异步返回值是另一段逻辑的判断条件。使用 async&await 将使代码可读性变得更好。
// Promise
function getInfo() {
getValue().then(
value => {
if (value === 22) {
return getInfo1().then(
data => {
// ...
}
)
} else {
return getInfo2().then(
data => {
// ...
}
)
}
}
)
}
// async await
async function getInfo() {
const value = await getValue()
if (value === 22) {
const data = await getInfo1()
// ...
} else {
// ...
}
}
处理中间值
异步函数常常存在一些异步返回值,很容易成为另一种形式的 “回调地狱”。
// Promise
function getInfo() {
getCode().then(
data => {
getLevel(data).then(
data => {
getInfo(data).then(
data => {
// todo
}
)
}
)
}
)
}
// async / await
async function getInfo() {
const code = await getCode()
const level = await getLevel(code)
const data = await getInfo(level)
// todo
}
对于多个异步返回中间值,搭配 Promise.all 使用能够提升逻辑性和性能。
// async / await & Promise.all
async function test() {
// ...
const [a, b, c] = await Promise.all([Fn1(), Fn2(), Fn3()])
const d = await Fn4()
// ...
}
靠谱的 await
await'str' 等于 await Promise.resolve('str'),await 会把任何不是 Promise 的值包装成 Promise
避免滥用 async&await
await 阻塞 async 函数中的代码执行,在上下文关联性不强的代码中略显累赘。
// async / await
async function init() {
render1(await get1())
render2(await get2())
}
// Promise
function init() {
get1()
.then(render1)
.catch(console.error)
get2()
.then(render2)
.catch(console.error)
}
错误处理
1、链式调用中尽量结尾跟 catch 捕获错误,而不是第二个匿名函数。因为规范里注明了若 then() 方法里面的参数不是函数则什么都不做,所以 catch(rejectionFn) 其实就是 then(null, rejectionFn) 的别名。
asyncFn().then(
successFn, // 产生的错误无法捕获
errorFn // `errorFn` 捕获 `asyncFn`
)
asyncFn () 抛出来的错误 errorFn 会正常接住,但是 successFn 抛出来的错误将无法捕获,所以更好的做法使用 catch。
asyncFn()
.then(successFn)
.catch(errorFn) // 尽量使用 `catch`
也可以通过 errorFn 来捕获 asyncFn() 的错误,catch 捕获 successFn 的错误。
作者: 咸鱼翻身