JavaScript中的Promise和async/await
一、同步与异步有何不同:
1.JavaScript是单线程的语言
2.异步不会阻塞代码执行
3.同步会阻塞代码执行
二、异步的应用场景:需要等待的情况
1.网络请求,如ajax图片加载
2.定时任务,如setTimeout
三、promise的基本使用:
1.通过串行的方式解决了回调地狱的问题
2.手写promise加载图片:
function loadImg(src) {
const p = new Promise(
(resolve, reject) => {
const img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
const err = new Error(`图片加载失败 ${src}`)
reject(err)
}
img.src = src
}
)
return p
}
const url1 = 'https://img.mukewang.com/5a9fc8070001a82402060220-140-140.jpg'
const url2 = 'https://img3.mukewang.com/5a9fc8070001a82402060220-100-100.jpg'
loadImg(url1).then(img1 => {
console.log(img1.width)
return img1 // 普通对象
}).then(img1 => {
console.log(img1.height)
return loadImg(url2) // promise 实例
}).then(img2 => {
console.log(img2.width)
return img2
}).then(img2 => {
console.log(img2.height)
}).catch(ex => console.error(ex))
四、promise的状态:
1.三种状态:
(1)pending(过程中)、resolved(解决了,也叫fulfilled)、rejected(失败了)
(2)pending => resolved 或 pending => rejecte
(3)变化不可逆
// 刚定义时,状态默认为 pending
const p1 = new Promise((resolve, reject) => {
})
// 执行 resolve() 后,状态变成 resolved
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
})
})
// 执行 reject() 后,状态变成 rejected
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject()
})
})
2.状态的表现和变化:
(1)pending状态,不会触发then和catch
(2)resolved状态,会触发后续的then回调函数
(3)rejected状态,会触发后续的catch回调函数
// 直接返回一个 resolved 状态
Promise.resolve(100)
// 直接返回一个 rejected 状态
Promise.reject('some error')
3.then和catch对状态的影响:
(1)then正常情况下返回resolved,里面有报错则返回rejected
(2)catch正常情况下返回resolved,里面有报错则返回rejected
// then() 一般正常返回 resolved 状态的 promise
Promise.resolve().then(() => {
return 100
})
// then() 里抛出错误,会返回 rejected 状态的 promise
Promise.resolve().then(() => {
throw new Error('err')
})
// catch() 不抛出错误,会返回 resolved 状态的 promise
Promise.reject().catch(() => {
console.error('catch some error')
})
// catch() 抛出错误,会返回 rejected 状态的 promise
Promise.reject().catch(() => {
console.error('catch some error')
throw new Error('err')
})
五、promise题目:
// 第一题,答案是1、3
Promise.resolve().then(() => {
console.log(1)
}).catch(() => {
console.log(2)
}).then(() => {
console.log(3)
})
// 第二题,答案是1、2、3
Promise.resolve().then(() => { // 返回 rejected 状态的 promise
console.log(1)
throw new Error('erro1')
}).catch(() => { // 返回 resolved 状态的 promise
console.log(2)
}).then(() => {
console.log(3)
})
// 第三题,答案是1、2
Promise.resolve().then(() => { // 返回 rejected 状态的 promise
console.log(1)
throw new Error('erro1')
}).catch(() => { // 返回 resolved 状态的 promise
console.log(2)
}).catch(() => {
console.log(3)
})
六、async/await:
1.背景:
(1)异步回调的callback hell
(2)Promise then catch链式调用,但也是基于回调函数
(3)async/await是同步语法,消灭回调函数
2.基本使用:
function loadImg(src) {
const promise = new Promise((resolve, reject) => {
const img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
reject(new Error(`图片加载失败 ${src}`))
}
img.src = src
})
return promise
}
async function loadImg1() {
const src1 = 'http://www.imooc.com/static/img/index/logo_new.png'
const img1 = await loadImg(src1)
return img1
}
async function loadImg2() {
const src2 = 'https://avatars3.githubusercontent.com/u/9583120'
const img2 = await loadImg(src2)
return img2
}
(async function () {
// 注意:await 必须放在 async 函数中,否则会报错
try {
// 加载第一张图片
const img1 = await loadImg1()
console.log(img1)
// 加载第二张图片
const img2 = await loadImg2()
console.log(img2)
} catch (ex) {
console.error(ex)
}
})()
3.async/await和Promise的关系:async/await可以消灭异步回调,但它和Promise并不互斥,两者相辅相成
(1)执行async函数,返回的是Promise对象(如果函数内没返回Promise对象,或者直接返回一个数值,则自动封装成Promise对象)
(2)await相当于Promise的then
A.可以声明一个变量接收函数的返回值:
这里的data2接收了fn1的返回值
B.await后面可以跟Promise对象、数值、一个async函数的返回值(async函数的返回值相当于Promise对象):
C.await后面跟rejected的Promise对象,则不会执行,所以要用try...catch捕获:
!(async function () {
const p3 = Promise.reject('some err')
const res = await p3
console.log(res) // 不会执行,因为await相当于then
})()
D.await是同步写法,但本质还是异步调用。只要遇到了await,后面的代码都相当于放在 callback 里:
async function async1 () {
console.log('async1 start')
await async2()
console.log('async1 end') // 关键在这一步,它相当于放在 callback 中,最后执行
}
async function async2 () {
console.log('async2')
}
console.log('script start')
async1()
console.log('script end')
(3)用try...catch可捕获异常,代替了Promise的catch
!(async function () {
const p4 = Promise.reject('some err')
try {
const res = await p4
console.log(res)
} catch (ex) {
console.error(ex)
}
})()
用try...catch捕获异常使语法更加标准化(像其他语言一样)
4.总结:
(1)async函数封装了Promise
(2)await相当于Promise的then,处理Promise成功的情况
七、async/await的本质:
1.async/await可以消灭异步回调
2.JavaScript还是单线程,还是得有异步,还是得基于事件循环(event loop)
3.async/await只是语法糖,只是从语法层面解决了异步回调
八、async/await题目:
1.第一题,答案:a是一个promise对象,b是100,因为async函数返回一个promise,而await相当于promise里面的then
2.第二题,答案:先打印start,然后a是100,b是200,执行到打印c会报错,然后后面的代码都不会执行了,因为await相当于then,只执行resolved,而这里是rejected,需要try..catch来执行
3.第三题,答案:依次打印script start、async1 start、async2、promise1、script end、async1 end、promise2、setTimeout,原因:
(1)setTimeout是宏任务
(2)await后面的都作为回调内容,是微任务
(3)初始化promise时,传入的函数会立刻被执行
(4)then后面的回调是微任务
4.第四题(请使用Promise.all并行请求数据):
const getInfo = async(qq, callback) => {
let baseInfo
let payInfo
try {
let uidData = await axios.get('http://test.com/getUid', { params: { qq } })
if(uidData.ret === 0) {
let uid = uidData.uid
let [baseInfoData, payInfoData] = await Promise.all([
axios.get('http://test.com/getBaseInfo', { params: { uid } }),
axios.get('http://test.com/getPayInfo', { params: { uid } })
])
if(baseInfoData.ret === 0) { baseInfo = baseInfoData.info } else { baseInfo = {} }
if(payInfoData.ret === 0) { payInfo = payInfoData.info } else { payInfo = {} }
callback(Object.assign({}, baseInfo, payInfo))
} else {
callback({})
}
} catch(err) {
throwError(err)
}
}
getInfo(123456, function(data) {
console.log(data)
})