前言
这一部分的理解在JavaScript的中也是比较复杂的东西,虽然对我来说,现阶段还没有什么大的影响,但实际上已经隐隐约约对我解决问题看待的思路和学习中有影响了。外加上一个师妹对这一部分很是迷惑,作为师兄证明能不挺身而出,手把手教学~~~~加上快要国庆了,上班摸鱼的时间多了,怎么能错过整理学习,铲除心中的迷惑的好机会呢
一、为什么需要异步
首先我们的JavaScript是单线程的。这必然导致很多的不便利,我们总不能等着前面的人慢慢的处理完它的事情才轮到我们,这样带浪费资源了、
举个例子:有客人来你家做客了,你爸叫你去泡茶,你需要洗杯子(3min),烧开水(15min),冲茶叶(1min),准备点心(2min)。那么要是单线程下来,是不是需要(3+15+1+2=21min)但实际上我们只需要16min,这就是在数学上的统筹,我们在一开始就烧开水,这个时间里,我们就可以做其他的事情,等到开水好了,其他的也就做完了,也就剩下冲茶叶了。
这就是我们需要对JavaScript进行优化调整的地方,也就是用到异步的思想。
再有个例子就是我们向服务器请求数据,我们不可能说等他好了才执行下面的操作,我们对请求的接口,往往会使用到ansyc 和 awite 来处理。
二、异步的过程
首先,我们来了一个任务,我们会先判断他是同步还是异步,同步的会直接进入到主线程执行,异步的会进入到任务队列,等待主线程的全部执行完后,再把任务队列的任务加载到主线程进行处理
如下:
function load (src, resolve) {
let script = document.createElement('script')
script.src = src;
script.onload = resolve;
document.body.appendChild(script);
}
//a();
//b();
load("a.js", () => {
a();
})
load('b.js', () => {
b();
})
console.log('------->666666');
我们创建加载脚本,脚本是“异步”调用的,因为它从现在开始加载,但是在这个加载函数执行完成后才运行。
所以这里会优先执行输出666666,再来执行js的,因为后两者属于异步,同时我们会发现这里a和b的输出顺序会有时不同,这是应为他们是按照谁先加载完成,谁就先加入到任务队列中的。同时我们不能直接调用脚本里面的方法,因为我们不知道脚本什么时候加载完成,我们需要把调用写到回调函数里面去。
那么我们要是想先输出a再来输出b呢,或者说a的值依赖于b的返回值呢?这里就要嵌套回调,那么要把b写到a的回调函数里面了。这样我们可想而知,当只是一两个的时候,我们还比较好处理,但是当我们在b中还需要调用一个c呢?c中再调用一个d呢?…这时候,非常的不便于维护,代码的层次也变得很深,回调地狱就出现了~~~
为了解决上面的问题,我们就有了promise(期约)的出现
三、promise(期约)
1.期约状态
首先期约有是三个状态
- 待定(pending)
- 兑现(fulfilled)
- 拒绝(rejected)
console.log(new Promise((resolve, reject) => {}));
console.log(new Promise((resolve, reject) => {
resolve('成功')
}));
console.log(new Promise((resolve, reject) => {
reject('失败')
}));
结果如图:
2.期约的注意点
Promise有几个点要注意的:
- 三个状态,能且只能处于其中之一。
- 状态的改变通过执行器函数改变,通常命名为resolve 和 reject,状态改变为兑现和拒绝。
- Promise 解决的是异步编码风格的问题,不是其他的问题~~~
promise的使用
- then:为期约实例添加处理程序
- catch:为期约实例添加拒绝处理程序
- finally:这个是无法知道期约状态的,所以一般用来清理代码
let p = new Promise((resolve, reject) => {
console.log('initial promise reject');
reject()
});
p.catch(()=> console.log('reject handler'))
.then(()=> console.log('resolve handler'))
.finally(()=> console.log('finally handler'))
console.log('1');
结果如图:
在 new Promise()的时候,Promise的执行器就会立马执行
输出:initial promise reject
但是调用reject()会触发异步操作,传入的then()方法的函数会被添加到任务队列并异步执行
所以先输出:1,才有后面的输出。
使用promise,它的三个方法都是返回期约的,所以有效的解决了回调地狱的问题。
3.微任务
那么promise里添加的任务会添加到哪里去呢?其实这里还有一个叫微任务的概念
我们把消息队列中的任务称为宏任务,每个宏任务中都包含了一个微任务队列,等宏任务中的主要功能都直接完成之后,这时候,渲染引擎并不着急去执行下一个宏任务,而是执行当前宏任务中的微任务先。
总结来说:每一个宏任务都有对应的微任务,该宏任务的微任务会在下一个宏任务执行前先执行,微任务的执行时长会影响计算到当前宏任务的执行时长。当当前的宏任务回调创建到宏任务和微任务时,微任务会先执行
四、await/async
await/async是对promise的进一步优化,Promise 的编程方式,当比较复杂的情况,会有大量的 then 方法,虽然解决了回调地狱的问题,但是在语义,阅读性等,还是很不便,这就是 async/await 出现的原因,使用 async/await 可以实现用同步代码的风格来编写异步代码
async
用来声明异步函数,使其具有异步特性,返回的是一个期约对象
await
可以暂停异步函数的执行,等待期约的解决
红宝石上面的一个例子:
async function foo() {
console.log(2);
console.log( await Promise.resolve(8));
console.log(9);
}
async function bar() {
console.log(4);
console.log( await (6));
console.log(5);
}
console.log(1);
foo();
console.log(3);
bar();
console.log(5);
//输出结果:1 2 3 4 5 6 7 8 9
(1) 打印1;
(2) 调用异步函数foo();
(3) (在foo()中)打印2;
(4) (在foo中) await 关键字暂停执行,向消息队列中添加一个期约在落定之后执行的任务
(5) 期约立即落定、把给 await 提供的任务添加到消息队列;
(6) foo()退出
(7) 打印3;
(8) 调用异步函数bar();
(9) (在bar()中)打印4;
(10) (在bar()中) await关键暂停执行,为立即可用的值6向消息队列中添加一个任务;
(11) bar()退出;
(12) 打印5
(13) 顶级线程执行完毕;
(14) javaScript行时从消息队列中取出解决 await期约处理程序,并将解决的值8提供给它;
(15) JavaScript行时向消息队列中添加一个恢复执行foo()函数的任务;
(16) JavaScript行时从消息队列中取出恢复执行bar()的任务及值6;
(17) (在bar()中)恢复执行, await取得值6;
(18) (在bar()中)打印6;
(19) (在bar()中)打印7
(20) bar()返回;
(21) 异步任务完成, JavaScript消息队列中取出恢复执行foo()的任务及值8;
(22) (在foo()中)打印8;
(23) (在foo()中)打印9;
(24) foo()返回。
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。