JS异步编程(async、await详解)

// 执行输出结果:Promise的结构体中也是主线程、主线程、微任务 Promise、宏任务 setTimeout

知识点(细节)

  • 执行顺序:主线程>微任务>宏任务

  • Promise 的函数体也是主线程,并不是 Promise 的所有都是微任务,只有他resolve()reject()的回调部分才是微任务

  • 即使setTimeou()的延迟时间是 0,并且写在最前面,也是得等主线程和微任务执行完了之后才会去执行

  • Promise 的状态是单向的,不可逆的,一旦触发了一个状态之后就不可以进行更改状态了

Promise 的 then()方法细节

Promise()的 then()方法返回的值默认也是一个 Promise

new Promise((resolve, reject) => {

resolve(‘请求成功1’)

})

.then(

res => {

return ‘请求成功’ // 只要then 里面有return 结果就可以继续使用then来接收

},

err => {}

)

.then(res => {

console.log(‘kkk’, res)

})

// 执行结果 kkk 请求成功

  • 只要 then 里面有 return 结果就可以继续使用 then 来接收,不论是第一个成功的回调的 return 还是 第二个失败的回调的 return 只要在 then 一下就可以拿到那个 return 的值

new Promise((resolve, reject) => {

resolve(‘请求成功1’)

})

.then(

res => {

return new Promise((resolve, reject) => {

reject(‘失败了’)

})

},

err => {}

)

.then(null, err => {

console.log(‘失败的结果’, err)

})

//6-Promise的then.html:31 失败的结果 失败了

  • 正常的使用,then 回调里面 return 一个新的 Promise,这样因为 return 的是一个 Promise,所以我们就可以继续的链式的在后面继续一直的 then 下去了

Promise 多种错误监听方式与 catch 总监听

Promise 的 resolve 和 reject 状态分别可以在 then 方法的两个回调函数中捕获,但是除此之外还可以使用 catch()函数中来进行总获取

let p1 = new Promise((resolve, rejetc) => {

// resolve(‘处理成功’)

rejetc(‘处理失败’)

})

p1.then(res => {

return new Promise((resolve, reject) => {

reject(‘error’)

})

}).catch(err => {

console.log(‘catch总接收’, err)

})

// 打印结果:catch总接收 处理失败

知识点:

  • catch 是对整个 promise 流的一个总的错误监听,如果在 then 中没有写第二个错误处理的函数,那么这个错误会走到 catch 这里

  • 因为 catch 是可以实现对整个 Promise 流的监听错误,所以 catch 正常情况下都是写在最后面的

  • 如果一个 promise 的 then 中有对错误的处理,那么执行的顺序是会选择走自己的错误处理,原因也很好理解,自己有错误处理就用自己的,没有就用公用的

let p2 = new Promise((resolve, reject) => {

jimmy()

// try {

// jimmy()

// } catch (error) {

// reject(error)

// }

})

p2.then(

res => {

console.log(res)

},

err => {

console.log(err.message)

}

)

// 输出结果:jimmy is not defined

的确是存在逻辑错误,调用了一个没有定义的函数,但是我们会发现自动的走到了错误处理函数上,我们可以理解是在 promise 内部封装了我们看不见的 try-catch 模块,进入 catch 的时候会自动的 reject 出来错误信息

小案例 – 使用 Promise 封装一个 ajax

使用 ajax 就避免不了回调地狱,所以使用 Promise 来封装一下 ajax 是一个优解,axios 就是通过 Promise 封装得来的

// 要有面向对象思想,万物皆可封装成为一个我们自己的东西

class HttpError extends Error {

constructor(msg) {

super(msg) // 这里的msg 会被默认放在 message 属性里面

this.name = ‘接口错误’

}

}

class ParamsError extends Error {

constructor(msg) {

super(msg)

this.name = ‘参数错误’

}

}

function ajax(url) {

return new Promise((resolve, reject) => {

if (!/^https?/.test(url)) {

reject(new ParamsError(‘请求地址错误’))

}

let xml = new XMLHttpRequest()

xml.open(‘GET’, url)

xml.send()

xml.onload = () => {

console.log(‘kkk’, xml)

// 箭头函数this指向的是 调用这个函数对象的this

if (xml.status == 200) {

resolve(xml.response)

} else if (xml.status == 404) {

reject(new HttpError(‘请输入正确的接口’))

} else {

reject(‘加载失败’)

}

}

})

}

ajax(‘https://jsonplaceholder.typicode.com/posts’)

.then(res => {

console.log(res)

})

.catch(err => {

// instanceof 能够判断该对象对象是从哪个类实例出来的

/*

最典型的就是可以用来判断是对象还是数组

*/

if (err instanceof ParamsError) {

console.log(err)

} else if (err instanceof HttpError) {

alert(err)

}

})

总结:

  • 这个案例虽小,但是涉及到的东西还是很多的,如创建自己的错误类,箭头函数的指向,创建基础的 ajax,instanceof关键字的使用

  • Array 是继承 Object 类的 object 对象是通过实例化 Object 实现的,所以我们呢可以通过 对象是否是通过 Array 实例化出来的 来判断是否数组还是普通对象

finally 关键名

finally 表示无论 promise 请求的状态是成功还是拒绝都会执行的的代码块,就十分的类似 try-catch-finally

  • 这个关键字可以做很多的操作,一个比较常用的操作就是可以封装一个加载动画,每次执行异步请求的时候显示这个动画,finally 的时候将这个动画给取消掉。

封装定时器

function timeout(delay) {

return new Promise((resolve, reject) => {

setTimeout(resolve, delay)

})

}

// 使用promise 不会造成会回调地狱 是一个竖装从下到下的执行过程 整个过程十分清晰

timeout(2000)

.then(() => {

console.log(‘使用Promise’)

return timeout(2000)

})

.then(() => {

console.log(‘封装的定时器’)

})

// 使用原生setTimeout 容易造成回调地狱 不好处理

setTimeout(() => {

console.log(‘不封装’)

setTimeout(() => {

console.log(‘回调地狱’)

}, 2000)

}, 2000)

封装轮询器

function interval(delay, callback) {

return new Promise((resolve, reject) => {

let id = setInterval(() => {

callback(id, resolve)

}, delay)

})

}

// 一旦理解了callback的使用 就更能理解promise的使用了 这样封装之后将setInter

interval(1000, (id, resolve) => {

console.log(‘hello wolrd’)

clearInterval(id)

resolve(‘封装成功’)

}).then(res => {

console.log(‘promise’, res)

})

Promise.resolve 与 Promise.reject 接口

Promise.resolve()是 promise 提供的一个直接获取成功状态的接口,使用的点就在于不用写上完整的 promise

Promise.reslove(‘success’).then(res => {

console.log(res) // success

})

Promise.reject(‘error’).then(null, err => {

console.log(err) // error

})

new Promise((reslove, reject) => {

reslove(‘成功a’)

})

.then(res => {

if (res != ‘成功’) {

throw new Error(‘不为成功!’)

}

})

.catch(err => {

console.log(‘anser1’, err)

})

// Promise.reject 的用途在于可以直接返回一个失败的额状态, 我们可以再第二个回调或者错误中捕获到错误信息

new Promise((resolve, reject) => {

resolve(‘成功b’)

})

.then(res => {

if (res != ‘成功’) {

// 当不是我们想要的状态 我们可以直接使用Promise.reject返回一个错误状态

return Promise.reject(‘不为成功’)

}

})

.catch(err => {

console.log(‘anser2’, err)

})

  • 这两个接口不是必须的,如果没有这两个 Promise 也是照样使用

  • 用途也有,比如缓存数据,当向后端获取一个基本不变的数据的时候,就可以判断是否缓存中有数据,如果有就直接走Promise.resolve(缓存数据)这样就可以直接拿到数据。

Promise.all()

Promise.all()Promise 提供的一个批量处理 Promise 的一个接口,

let promise1 = new Promise((resolve, reject) => {

setTimeout(() => {

reject(‘失败1’)

}, 1000)

}).then(null, err => {

console.log(‘接收到了失败’)

// 因为对失败进行了处理 所以默认会返回一个成功状态的promise

})

let promise2 = new Promise((resolve, reject) => {

setTimeout(() => {

resolve(‘成功2’)

}, 1000)

})

Promise.all([promise1, promise2])

.then(res => {

console.log(‘success’, res)

})

.catch(err => {

console.log(‘error’, err)

})

// success (2) [“成功1”, “成功2”]

  • promise.all()参数是传递一个数组 - 这个数组每个成员都是一个 Promise,只有数组的 Promise 的状态都是成功的状态 才会走到成功的回调

  • Promise 无论是成功的处理还是失败的处理 默认返回的都是一个成功的状态

Promise.allSettled()

Promise.allSettled()也是一个用来批量处理 Promise 的一个接口

let promise1 = new Promise((resolve, reject) => {

setTimeout(() => {

reject(‘失败了’)

}, 1000)

})

let promise2 = new Promise((resolve, reject) => {

setTimeout(() => {

resolve(‘成功了’)

}, 1000)

})

Promise.allSettled([promise1, promise2]).then(res => {

console.log(‘最终的数据’, res)

/*

(2) [{…}, {…}]

0: {status: “rejected”, reason: “失败了”}

1: {status: “fulfilled”, value: “成功了”}

length: 2

proto: Array(0)

*/

})

  • Promise.allSteeled()和Promise.all()的却别在于,前者返回的状态永远都是成功的状态

  • Promise.allSettled()参数传递的也是一个 Promise 数组,无论是成功还是失败的状态,都会走到 then 里面

  • Promise.allSettled()返回的是一个对象数组,其中对象包含了 status:状态,reson:失败原因,value 成功的值

Promise.race()

Promise.race() 传递一个数组 数组里面的 Promise 哪个快就会先执行哪个

// 封装带有请求超时处理的函数

function query(url, delay = 3000) {

let promise1 = axios.get(url)

let promise2 = new Promise((resolve, reject) => {

setTimeout((resolve, reject) => {

reject(‘请求超时’)

}, delay)

})

return Promise.race([promise1, promise2])

}

query(‘http://127.0.0.1:666’)

.then(res => {

console.log(‘请求结果’, res)

})

.catch(err => {

console.log(‘请求超时了请重试’)

})

async await 语法糖

async`await`是 promise 提供的语法糖,本质上也是一个 promise

注意点:

  • 一个 function 如果加上 async 关键字修饰之后,调用这个函数,这个函数会默认返回一个解决状态的 promise,原理是因为 Pormise 的无论是 then 还是 catch 都是会默认返回一个解决状态的 Promise

async 本质

async function promise() {}

console.log(promise()) // Promise {: undefined}

function normal() {

return Promise.resolve()

}

console.log(normal()) // Promise {: undefined}

  • async 链式操作

async function Promise2() {

let res = await new Promise((resolve, reject) => {

setTimeout(() => {

resolve(‘请求成功’)

}, 2000)

})

/*

await 默认会返回一个 promise 就可以继续一直的 await 下去 虽然是本质上是异步 但是代码的执行顺序是同步执行的

*/

let res2 = await new Promise((resolve, reject) => {

setTimeout(() => {

resolve(‘请求成功2’)

}, 2000)

})

console.log(‘结果’, res2)

}

Promise2()

// 过4秒会输出 结果 请求成功2

使用async await语法糖的好处在于 和传统的 Promise 相比 我们可以不用一直写 .then()这样的回调函数, 要获取值直接一个 await 来接收返回值即可

async、await 监听错误

  • 函数只要加上了 async 关键字之后,默认就是会返回一个 promise,因为,我们在调用函数的时候就可以使用 catch 来进行捕获

  • 在 async 内部如果想监听 await 部分的错误,就需要使用传统的 try-catch 模块来进行监听错误

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
直写 .then()这样的回调函数, 要获取值直接一个 await 来接收返回值即可

async、await 监听错误

  • 函数只要加上了 async 关键字之后,默认就是会返回一个 promise,因为,我们在调用函数的时候就可以使用 catch 来进行捕获

  • 在 async 内部如果想监听 await 部分的错误,就需要使用传统的 try-catch 模块来进行监听错误

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-VzjKUhjd-1715545876336)]

[外链图片转存中…(img-cSjiTz2K-1715545876336)]

[外链图片转存中…(img-4CbIRZps-1715545876337)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值