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

  1)基本原则:
    await 后面跟 Promise 对象:会阻断后续代码,等待状态变为 resolved ,才获取结果并继续执行
    await 后续跟非 Promise 对象:会直接返回
  2)详细使用:

    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成功的情况

(3)要用try...catch处理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)
})

 

posted @ 2021-05-06 18:53  starlog  阅读(1302)  评论(0编辑  收藏  举报