=========================事件循环==================
先看以下的例子:
例1:
console.log("a");
setTimeout(() => {
console.log("b");
}, 0)
console.log("c");
// 以上的结果是 先输出 a, c, b
// 然后看下面的例子:
console.log("a");
setTimeout(() => {
console.log("b");
}, 0)
for(let i = 0; i < 1000; i ++){
console.log("c")
}
// 以上的结果是 a 1000个c 最后是 b
问:为什么总都是b在最后呢?
事件回顾:
JS运行的环境称之为宿主环境。
执行栈:call stack , 一个数据结构, 用于存放各种函数的执行环境,每一个函数执行之前,
它的相关信息会加入到执行栈。 函数调用之前, 创建执行环境, 然后加入到执行栈; 函数调用之后,销毁执行环境
JS引擎执行的都是栈的最顶部,执行完后,顶部的执行上下文会销毁(出栈),
调用函数之前一定要先入栈(创建一个对应的上下文),执行完后出栈,销毁对应的上下文
异步函数: 某些函数不会立即执行,需要等到某个时机到达后才会执行,这样的函数称为
异步函数,比如:事件处理函数,setTimout,setTimeItervel等。异步函数的执行时机,会被宿主环境控制。
浏览器宿主环境中包含5个线程:
1. JS引擎: 负责执行栈的最顶部代码
2. GUI线程: 负责渲染页面
3. 事件监听线程: 负责监听各种事件
4. 计时线程: 负责计时, 如setTimeOut, setTimeInterval等
5. 网络线程: 负责网络通信, 如 ajax, axios等
当上面的线程发生某些事情,如果该线程发现,这件事情有处理程序,他会将该处理程序加入
到一个叫做事件队列的内存中。 当JS引擎发现,执行栈中已经没有了任何内容后,会将事件队列中
的第一个函数加入到执行栈中执行。
JS引擎对事件队列的取出方式,以及与宿主环境的配合,称之为事件循环。
事件队列在不同的宿主环境中有所差异,大部分宿主环境会将事件队列进行细分。
在浏览器中,事件队列分为两种:
宏队列: macroTask, 计时器结束的回调,事件回调,http回调等绝大部分异步函数进入宏队列
微队列: mutationObserver, Promise产生的回调进入微队列
mutationObserver: 用于监听dom里面属性或者结构发生变化时,dom发生变化。
当执行栈清空时, JS引擎首先会将微队列中的所有任务依次执行结束,如果没有微队列的任务,则执行宏队列里面的任务
=================es6异步处理 Promise=================
事件和回调函数的缺陷:
我们习惯于使用传统的回调或事件处理来解决回调
事件: 某个对象的属性是一个函数,当发生某一事件时,运行该函数
dom.onclick = function(){}
回调: 运行某个函数以实现某个功能的时候,传入一个函数作为参数,当发生某件事的时候,会运行该函数
dom.addEventLinster("click",function(){})
本质上,事件和回调并没有本质的区别,只是函数放置的位置不同而且。
该模式主要面临以下问题:
1. 回调地狱:某个异步操作需要等待之前的异步操作完成,无论用回调还是事件,都会陷入不断的嵌套
2. 异步之间的联系: 某个异步操作要等待多个异步操作的结果,对这种联系的处理,会让代码的复杂度剧增
异步处理的通用模型
ES 官方参考了大量的异步场景,总结一套异步的通过模型,该模型可以覆盖几乎所有的异步场景,甚至同步场景
值得注意的是,为了兼容旧系统,ES6 并不打算抛弃过去的做法,只是基于该模型推出的一个权限的 API, 使用该APi, 让异步处理更加的简洁优雅
理解该API, 最重要的是,理解他的api
1. ES6 将某一件可能发生异步操作的事情,可以分为两个阶段: unsettled 和 settle
unsettled: 未决阶段,表示事情还在进行前提的处理,并没有发生通向结果的那件事;
settled: 已决阶段, 事情已经有了一个结果,不管这个结果是好是坏,整件事情无法逆转;
事情总是从 未决阶段 逐步发展到 已决阶段的。 并且,未决阶段拥有控制何时通向已决阶段的能力
2. ES6 将事情划分为三种状态: pending, resolved rejected
pending: 挂起,处于未决阶段,则表示这件事情还在挂起(最终的结果还没有出来)
resolved: 已处理, 已决阶段的一种状态,表示整件事情出现了结果,并且是正常逻辑进行下去的结果
rejected: 已拒绝, 已决阶段的一种状态,表示整件事情出现结果,并不是一个正常的结果,错误的结果
既然未决阶段有权决定事情的走向,因此,未决阶段可以决定事情最终的状态!
我们 把事情变为resolved状态的过程叫做:resolve,推向该状态时候,可能还会传递一些数据。
我们 把事情变为rejected状态的过程叫做:rejected,推向该状态时候,可能还会传递一些数据,一些错误的信息。
无论是哪个阶段,过程都是不可逆的
3. 当事情的处理到达已决状态, 不同的状态决定不同的处理
resolved状态: 这是一个正常的已决的状态,后续处理表示未thenable
rejected状态:这个一个非正常的已决的状态,后续处理表示未catchable
后续处理可能有多个,因此会形成作业队列,这些后续处理按照顺序,当状态到达后依次执行
Promise API:
promise 不是消除回调,而是将回调用两种状态来返回
使用方法:
const pro = new Promise((resolve, reject) => {
// 未决阶段,也可以理解为等待阶段,异步之前做的事情,代码在这里写
// 通过调用resolve函数将Promise推向已决阶段的resolve状态(成功)
// 通过调用reject函数将Promise推向已经阶段的reject状态(失败)
// resolve 和rejecte 均可以传递最多一个参数,表示推送状态的数据
})
pro.then(data => {
// 这是thenable 函数, 如果当前的Promise已经是resolved状态,该函数会立即执行,
// 如果当前是为决状态,则会加入到作业队列,等待到达resolved状态后执行
// data为resolved 的状态数据
}, err => {
// 这是catchable 函数, 如果当前的Promise已经是rejected状态,该函数会立即执行
// 如果当前是为决状态,则会加入到作业队列,等待到达rejected状态后执行
// err 为rejected 的状态数据
})
细节:
1. 未决阶段的函数的代码是同步代码的,会立即执行
2. thenable 和catchable 函数是异步的,就是放到立即执行,但是必须需要等到同步代码执行完后,才会执行,并且是加入的是微队列里面
3. pro.then() 可以单独thenable的函数, 也可单独添加catchable的函数, 如 pro.catch();
4. 在未决状态中发生错误或者抛出错误,会将错误推向reject,并且会被catchable捕获
5. 一旦状态推向已决,状态不可以改变
6. promise并没有消除回调, 只是让回调变的可控
=======================Promise 串联======================
当后续的promise需要用到之前promise产生的结果;
Promise 无论then方法,还是catch方法, 返回的是一个全新的Promise对象,状态满足下列规则:
1. 返回的Promise对象如果是挂起状态(未决),新的Promise的状态也是挂起状态
2. 如果当前的Promise是已决状态, 会运行后续的函数,并将后续处理函数的结果(返回值)
作为resolved状态数据,会应用奥新的Promise中; 如果后续处理函数发生错误,则把返回值当作
rejected状态数据,应用到新的Promise中(后续的Promise,需要等到前面的promise已决)
如果前面的Promise的后续处理,返回的是一个Promise, 那么后面的Promise的状态和前面的状态信息保持一致
const pro = new Promise((resolve, reject) => {
resolve(1);
});
const pro1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(3);
}, 3000)
});
pro.then(result => {
console.log("第一个promise的状态")
console.log(result) // 1
return pro1;
}).then(result => {
console.log(result) // 3 下面是undefined,是因为这里没有返回
}).then(result => {
console.log(result) //undefined
})
// 输出结果 3 和 undefined 在3秒后打印
Promise的其他api
原型成员(实例成员):
then: 注册一个后续处理函数,当Promise为resolved状态时运行该函数
catch: 注册一个后续处理函数, 当Promise为rejected状态时运行该函数
finally:[es2018] 注册一个后续处理函数(无参),当Promise为已决时运行该函数
例如:
const pro = new Promise((resolve, reject) => {
resolve(1)
});
pro.finally(() => {
console.log("第一次finally的执行");
})
pro.then(res => {
console.log(res, "已决resolved得出的结果")
})
pro.catch(err => {
console.log(err, "已决reject得出结果")
})
pro.finally(() => {
console.log("已决第二次执行finally")
})
// 得出结果如下:
// 第一次finally的执行
// 1 已决resolved得出的结果
// 已决第二次执行finally
构造函数成员(静态成员)
resolve: 该方法返回一个resolved状态的Promise,传递的数据作为状态数据;
特殊情况:如果传递的数据是promise, 则直接返回传递的Promise对象
例如:
const pro = new Promise((resolve, reject) => {
// 这里面的代码是同步代码
resolve(1);
})
// 等效于
const pro = Promise.resolve(1);
特殊情况:
const pro = new Promise((resolve, reject) => {
// 这里面的代码是同步代码
resolve(1);
})
const pro1 = Promise.resolve(pro);
// 等效于
const pro1 = pro;
reject: 该方法返回一个rejectd状态的Promise,传递的数据作为状态数据
例如:
const pro = new Promise((resolve, reject) => {
// 这里面的代码是同步代码
reject(1);
})
// 等效于
const pro = Promise.reject(1);
all(iterable): 这个方法返回一个新的Promise对象, 该promise对象在所有promise数组中
所有的promise都已决resolved的时候触发成功方法, 一旦有任何一个promise里面的已决rejected状态
一行,会把错误立即返回;
例如:
function getRandom(min, max) {
return Math.floor(math.getRandom() * ((max - min) + min))
}
const proms = [];
for (let i = 0; i < 10; i++) {
proms.push(new Promise((resolve, rejecct) => {
setTimeout(() => {
resolve(i)
}, getRandom(1000, 5000));
}))
}
// 等待所有promise完成
Promise.all(proms).then(res => {
console.log("所有promise 已决resolved状态后执行")
})
race: 有一个成功那就成功,有一个失败那就失败,返回的是一个Promise
es6 异步处理之 Promise学习总结
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.