异步编程
1 promise
1.1 promise 的本质与用法
- promise是对象不是函数 new Promise
- promise的三个状态
- pending 状态,不会触发then和catch
- resolved状态,会触发后续的then回调函数,不会触发catch
- rejected状态,会触发后续的catch回调函数或者then的第二个回调函数
- 当同时有then的第二个回调函数和catch回调函数时,且是promise内部错误时,按就近原则,调用then的第二个回调函数
- 当同时有then的第二个回调函数和catch回调函数时,且不是promise内部错误时,例如网络中断,直接调用catch
1 2 3 4
| pending fulfilled rejected 进行中 已成功 已失败
new Promise时 promise处于进行中状态
|
- promise使用
- resolve()将进行中状态转换成已成功状态 reject()将进行中状态转换成已失败状态 且状态转换后不可逆
- 根据promise内部的异步操作的成功或失败来改变promise状态 ——————–1
- 若promise内部异步操作成功或失败有数据返回则用resolve(res) 、reject(err)返回数据———-2
- 若promise内部异步操作成功或失败没有数据返回则用resolve() 、reject()来控制promise状态的变化就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| function(){ return new Promise((resolve,reject)=>{ wx.getSystemInfo({ success:(res)=>{--------------------------------1 resolve(res) 或者 resolve() ----------------2 }, fail:(err)=>{-----------------------------------1 reject(err) 或者 reject()-----------------2 } }) }) }
text(){ function.then( (res)=>{},--------------------成功的回调函数---->对应于resolve()------3 (error)=>{}-------------------失败的回调函数---->对应于reject()-------3 ) }
async text(){ const res = await function() }
|
1 2 3 4 5 6 7 8
| Promise.resolve(100).then(res => {}) cosnt p = Promise.resolve(100) p.then(res => {})
Promise.reject('some error').catch(err => {}) cosnt p = Promise.reject('some error') p.catch(err => {})
|
1.2 promise.all
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| let p1 = new Promise( (resolve, reject) => { setTimeout( () => { console.log('p1') resolve('p1') },2000) })
let p2 = new Promise( (resolve, reject) => { setTimeout( () => { console.log('p2') resolve('p2') },1000) })
let p3 = new Promise( (resolve, reject) => { setTimeout( () => { console.log('p3') resolve('p3') },3000) })
Promise.all([p1,p2,p3]).then((res) => { sonsole.log('全部完成') console.log(res) }).catch((err) => { sonsole.log('失败') console,log(err) })
.all的返回值res是一个数组 对应于 [p1,p2,p3]的返回值
|
1.3 promise.race
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| let p1 = new Promise( (resolve, reject) => { setTimeout( () => { console.log('p1') resolve('p1') },2000) })
let p2 = new Promise( (resolve, reject) => { setTimeout( () => { console.log('p2') resolve('p2') },1000) })
let p3 = new Promise( (resolve, reject) => { setTimeout( () => { console.log('p3') resolve('p3') },3000) })
Promise.race([p1,p2,p3]).then((res) => { sonsole.log('完成') console,log(res) }).catch((err) => { sonsole.log('失败') console,log(err) })
|
1.4 promise 规避回调地狱
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 假设封装的model中有3个API接口(getApi1,getApi2,getApi3), 且它们是链式调用getApi1-->getApi2--->getApi3
const model = model() const promise = model.getApi1() promise.then((res) => {---------------------调用api1 console.log(res, 'api1') model.getApi2().then((res) => {---------------调用api2 console.log(res, 'api2') model.getApi3().then((res) => {----------------调用api3 console.log(res, 'api3') }) }) })
|
- promise正确使用规避回调地狱
- 前提:每个api调用都要是promise的,即每次api调用都要是返回的promise对象
- 关键是:不在内部调用下一个api且处理该api,而是只在内部调用,再将调用return到外部,在外部再处理
1 2 3 4 5 6 7 8 9 10 11 12 13
| const model = model() model.getApi1()-----------------------------调用api1 .then((res) => { console.log(res, 'api1') return model.getApi2()------------------调用api2 并return到外部 }) .then((res => {---------------------------在外部处理api2的调用 console.log(res, 'api2') return model.getApi3()------------------调用api3 并return到外部 }) .then((res)=>{----------------------------在外部处理api3的调用 console.log(res, 'api3') })
|
1.5 then 和catch改变状态
- 状态变化会触发 then catch
- pending 不会触发任何 then catch 回调
- resolved状态,会触发后续的then回调函数,不会触发catch
- rejected状态,会触发后续的catch回调函数,不会触发then
- then catch 会继续返回 Promise ,此时可能会发生状态变化!!!
- then 正常返回resolved ,里面有报错则返回rejected, 若没有被触发则忽略
- catch 正常返回resolved ,里面有报错则返回rejected,若没有被触发则忽略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| Promise.resolve().then(() => { console.log(1) }).catch(() => { console.log(2) }).then(() => { console.log(3) })
Promise.resolve().then(() => { console.log(1) throw new Error('erro1') }).catch(() => { console.log(2) }).then(() => { console.log(3) })
Promise.resolve().then(() => { console.log(1) throw new Error('erro1') }).catch(() => { console.log(2) }).catch(() => { console.log(3) })
|
2 async await
2.1 async await的使用及前提
- 异步调用的终极处理办法 异步的一般使用场景(读写文件、操作数据库、网络请求、定时器、DOM事件)
- 前提:——–等待的异步函数返回的必须是一个promise对象——-
- 使用
- 在函数头部用async
- 在异步函数前面写await用于等待异步函数的完成
1 2 3
| async text(){ const pro = await function() //-------function()函数返回的必须是一个promise对象 }
|
2.2 async字段的理解
- async将一个函数的返回值强制包装成一个Promise对象
- 若返回值本身就是一个Promise对象,则将这个Promise对象本身返回,不再包装
1 2 3 4 5 6 7 8 9 10 11
| async test(){ return 'hahahhaha' }
console.log(test()) 打印出的是一个promise对象
test().then((res)=>{ console.log(res) }) 打印出的是 'hahahhaha'
|
2.3 await字段的理解
- await 可以堵塞当前线程 等待异步调用完成
- await 是一个求值关键字 可求值promise 还可以求值表达式
- 求值promise 相当于解析了promise对象 直接拿到了对象成功回调返回的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| text(){ const pro = function() //-------function()函数返回的必须是一个promise对象 } 此时res是一个promise对象 需要用.then()方法拿到这个对象的成功数据 pro.then((res)=>{ console.log(res)----------1 })
async text(){ const pro = await function() //-------function()函数返回的必须是一个promise对象 } 此时的pro就是上面回调函数中的res 相当于直接拿到了数据 console.log(pro)---------------1
---------------1 打印的结果一样
|
2. 求值表达式
1 2 3 4 5 6
| async test(){ const a = await 100*100 console.loe(a) }
打印结果为10000
|
2.4 和 Promise 的关系
- then同await,只处理Promise的resolved状态,不能处理rejected和pending状态
- then接收pending状态,then不会触发,后续回调函数不会执行
- then接收rejected状态,then不会触发,且此时没有处理内部返回的错误,会报错
- 除非有then的第二个回调函数,则可以处理rejected
await 处理 Promise的resolved状态,不能处理rejected和pending状态
- await 接收pending状态,await不会触发,后续回调函数不会执行
- await 接收rejected状态,await不会触发,且此时没有处理内部返回的错误,会报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| !(async function () { const p1 = new Promise(() => {}) await p1 console.log('p1') })()
!(async function () { const p2 = Promise.resolve(100) const res = await p2 console.log(res) })()
!(async function () { const res = await 100 console.log(res) })()
!(async function () { const p3 = Promise.reject('some err') const res = await p3 console.log(res) })()
|
catch处理 Promise 的rejected状态,不能处理resolved和pending状态
try…catch同promise机制的catch 处理 Promise 的rejected状态,不能处理resolved和pending状态
- resolved和pending状态不会触发try…catch
1 2 3 4 5 6 7 8 9
| (async function () { const p4 = Promise.reject('some err') try { const res = await p4 console.log(res) } catch (ex) { console.error(ex) } })()
|
2.5 await的本质是异步
- 只要遇到了
await
,后面的代码都相当于放在 callback 里。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| async function async1 () { console.log('async1 start') await async2() console.log('async1 end') }
async function async2 () { console.log('async2') }
console.log('script start') async1() console.log('script end')
script start async1 start async2 script end async1 end
async function async1 () { console.log('async1 start') await async2() console.log('async1 end') await async3() console.log('async1 end 2') }
async function async2 () { console.log('async2') }
async function async3 () { console.log('async3') }
console.log('script start') async1() console.log('script end')
|
3 回调写法
回调写法很麻烦,每次都需要将回调函数传递给异步函数,这样才能实现堵塞结束后再执行下一个任务
1 2 3 4 5 6 7 8 9 10
| test(sCallback){ wx.request({ url: 'classic/latest', success:(res)=>{ sCallback(res) console.log(res) } }) }
|
1 2 3 4 5 6 7 8
| test((res)=>{ console.log(res) this.setData({ classic:res, likeCount:res.fav_nums, likeStatus:res.like_status }) })
|
- 以上写法等同于以下写法,只是上面写法才能实现任务分离
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| test(){ wx.request({ url: 'classic/latest', success:(res)=>{ console.log(res) this.setData({ classic:res, likeCount:res.fav_nums, likeStatus:res.like_status }) console.log(res) } }) }
test()
|
4 回调写法、promise写法、async await写法的比较
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| userAuthorized1() { promisic(wx.getSetting)() .then(data => { if (data.authSetting['scope.userInfo']) { return promisic(wx.getUserInfo)() } return false }) .then(data => { if (!data) return this.setData({ authorized: true, userInfo: data.userInfo }) }) },
async userAuthorized2(){ const data = await promisic(wx.getSetting)() if (data.authSetting['scope.userInfo']) { const res = await promisic(wx.getUserInfo)() const userInfo = res.userInfo this.setData({ authorized: true, userInfo }) } },
userAuthorized() { wx.getSetting({ success: data => { if (data.authSetting['scope.userInfo']) { wx.getUserInfo({ success: data => { this.setData({ authorized: true, userInfo: data.userInfo }) } }) } } }) },
|
5 for…of 异步循环
forEach((item) => {}) 同步执行
for(let i in list) i取到的是list数组的索引值, i取到的是list对象的键 同步执行
for(let item of list) item取到的是list数组的元素, item取到的是list对象的值 异步执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| function multi(num) { return new Promise((resolve) => { setTimeout(() => { resolve(num * num) }, 1000) }) }
function test1 () { const nums = [1, 2, 3]; nums.forEach(async x => { const res = await multi(x); console.log(res); }) } test1();
async function test2 () { const nums = [1, 2, 3]; for (let x of nums) { const res = await multi(x) console.log(res) } } test2()
|
6 Promise中的then第二个参数和catch的区别
- 当错误是promise内部错误时,会按就近原则处理即:
- 当Promise.then中有第二个回调函数时,执行then第二个函数
1 2 3 4 5 6 7 8 9
| let p=new Promise((resolve,reject)=>{ throw new Error('出错了') }) p.then(()=>{},err=>{ console.log('reject'+err); }).catch(err=>{ console.log('catch'+err); }) 结果:rejectError: 出错了
|
- 当Promise.then中没有第二个回调函数时,执行catch
1 2 3 4 5 6 7
| let p=new Promise((resolve,reject)=>{ throw new Error('出错了') }) p.then(()=>{}).catch(err=>{ console.log('catch'+err); }) 结果:catchError: 出错了
|
- 当错误不是promise内部错误时,例如网络中断,则需要用catch来处理
- 即使有then的第二个参数也不会进入,会直接进入catch
- 如果在then的第一个函数里抛出了异常,后面的catch能捕获到,而then的第二个函数捕获不到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Promise.resolve('10') .then(res => { console.log(res) console.log('resolve') throw new Error('hhhh') },err => { console.log(err) console.log('err') }).catch(erro =>{ console.log(erro) console.log('reject') })
10 resolve Error: hhhh at F:\code\test-font\test.js:5:9 reject
|