认识异步函数
-
同步函数: const sum1 =(a,b)=>{ return a+b } console.log('AAA'); console.log(sum(5,6)); conosle.log('BBB') 异步函数:封装一个函数,这个函数2秒后返回a+b的和 const sum2 =(a,b,callback)=>{ setTimeout(()=>{ callback(a+b) },2000) } console.log('AAA') sum2(2,3,(result)=>{ console.log(result) }) console.log('BBB') 错误事例: const sumAfter2s = (a, b) => { setTimeout(() => { return a + b; }, 2000); } 这里面的return是定时器的这个函数的返回值,外部函数没有返回值
- 异步(Asynchronouse):不阻塞CPU,CPU先执行别的程序,这段程序被挂起,等它执行完,执行他的回调函数
- 目前的结论:同步函数,自己有返回值,而异步函数没有返回值(个人理解:为什么没有返回值,要拿到他的返回值肯定是有其作用,作用就是有用处,而异步函数会等其他的程序执行完在执行,所以在返回值也没有其作用了)
认识回调黑洞
- 也叫做回调地狱,callback hell
- 回调套回调,层次很多,这样会产生回调黑洞
-
比如利用刚才的sum2这个函数,计算3,4的和,在把这个值与5相加,这些和在与6相加 三次计算两个数之和 sum2(3,4,(r1)=>{ console.log(r1) sum2(r1,5,(r2)=>{ console.log(r2) sum2(r2,6,(r3)=>{ console.log(r3) }) }) }) 这样的代码难以阅读,和维护,叫做回调黑洞
- 下面引入Promise,来解决回调地狱
Promise -写同步的方法,写异步
- 认识到生活中的电器缺少锲约
- 生活中的电器都是异步工作的,他们工作的时候人不会被阻塞,人可以干别的
- 这些电器在结束工作之后,干的事儿不同,通知形式也不同
- 洗衣机在结束工作后,播放春天在哪里,微波炉工作结束后滴的一声
- 通知形式不同的哲学就是:缺少契约
- Promise就是为了统一这种契约来的:Promise生而为契约
Promise的基本使用
-
原始: const sum2 =(a,b,callback)=>{ setTimeout(()=>{ callback(a+b) },2000) } 使用Promise: const sum2=(a,b)=>{ return new Promise((callback)=>{ setTimeout(()=>{ callback(a+b) },2000) }) } 这里重写了num2方法,不同的是, 这个函数返回Promise类的实例,在new Promise()的时候,要求传入真正的回调函数。 new Promise(()=>{})里面做异步语句,异步结束后,调用callback()函数,将a+b传回来 也就是说定义确实变的麻烦了,但是,调用它的时候,也换来了快感,此时的异步函数是有返回值的 async function main(){ console.log('AAAA'); const r1 = await sum2(3, 4); const r2 = await sum2(r1, 5); const r3 = await sum2(r2, 6); console.log(r1); console.log(r2); console.log(r3); console.log('BBBB'); } main(); es6规定,async是标记异步函数的,await后面可以等一个Promise的实例 有await语句的函数前面必须加async标记,并且箭头函数不行 Promise实例天生就有三种状态:pending(进行中),fulfilled(结束),rejected(失败). 一上来就处于pending状态,Promise只有它自己内部的语句,能够把它切换成filfilled的状态。 -什么时候切换呢? 就是new Promise()的时候传入的那个函数的形参被调用的时候,Promise状态会变为fulfilled。 同时resolve()函数中的参数,将成为这个函数的返回值
Promise实际运用
-
顺序读取文件的例子 原始: const fs = require('fs') fs.readFile('./a.txt',(err,nr1)=>{ console.log(nr1.toString()); fs.readFile('./b.txt',(err,nr1)=>{ console.log(nr2.toString()); fs.readFile('./c.txt',(err,nr1)=>{ console.log(nr3.toString()); }) }) }) 使用Promise干掉回调地狱,但是使用的时候都要封装 const doFile=(filename)=>{ return new Promise((resolve)=>{ fs.readFile(filename,(err,nr)=>{ resolve(nr.toString()) }) }) } 调用: async function main(){ const nr1 = await doFile('./a.txt') console.log(nr1); const nr2 = await doFile('./b.txt') console.log(nr2); const nr3 = await doFile('./c.txt') console.log(nr3) } main();
-
Ajax顺序读取接口1,2,3 原始地狱写法: $.get('jiekou1.java',(data1)=>{ alert(data1); $.get('jiekou2.java',(data2)=>{ alert(data2); $.get('jiekou3.java',(data3)=>{ alert(data3); }) }) }) 使用Promise解决,先封装一下 const qingqiu = (url)=>{ return new Promise((resolve)=>{ $.get(url,data=>{ resolve(data); }) }) } 封装挺恶心的,但是换回的是调用的快感 async function main(){ const nr1 = await qingqiu('jiekou1.java') alert(nr1); const nr2 = await qingqiu('jiekou2.java') alert(nr2); const nr3 = await qingqiu('jiekou3.java') alert(nr3) } 用Axios解决封装的麻烦,Axios这个包封装了Promise版的Ajax,把Aajx封装到了Promise里面了 <script>引入Axios包</script> async function main(){ const nr1 = await axios.get('jiekou1.php').then(data => data.data); const nr2 = await axios.get('jiekou1.php').then(data => data.data); const nr3 = await axios.get('jiekou1.php').then(data => data.data); alert(nr1); } main();
-
再vue中axios { data(){ return { } }, methods:{ async loadData(){ const jg = await axios.get('').then(data=>data.data); this.jg = jg async changePage(){ const jg =await axios.get('').then(data=>data.data); } async changePageSize(){ const jg =await axios.get('').then(data=>data.data); } } } }
理解Promise契约
-
将大家都统一了起来 const xiyifu = (脏衣服,洗衣粉) => { return new Promise((resolve) => { 进水、 进洗衣粉、 洗20分钟、 排水、 再进水、 漂水、 放水、 resolve(干净衣服); }); } const menfan = (冷饭) => { return new Promise((resolve) => { 加蒸汽、 再加蒸汽、 再加蒸汽、 resolve(热饭); }); } async function main(){ const a = await xiyifu('内裤'); const a = await menfan('米粒'); }
Promise方便调错
-
try...catch...捕获的“可能发生”的错误,不会影响程序正常执行 console.log(1); console.log(2); console.log(3); try{ console.log('★'); asdfasdflkajsdlfjk }catch(e){ } console.log(4); console.log(5); // 打印 1 2 3 ★ 4 5 异步函数不能被try...catch... const sumAfter2s = (a, b) => { return new Promise((resolve, reject) =>{ setTimeout(() => { if(a + b > 10){ resolve(a + b); }else { reject('两个数字太小') } }, 2000); }); }; sumAfter2s(3, 4).then(data => { console.log(data); }).catch(e => { console.log(e); })
面试题参考
-
只写一行语句,实现效果: async function foo(){ // 等到2秒 await _________________________________________________ alert('你好'); } foo(); 答案: <script type="text/javascript"> async function foo(){ // 等到2秒 const a = await new Promise((resolve) => { setTimeout(() => { resolve(34) }, 2000) }); alert(a); } foo(); </script>