(一):EventLoop、js的运行机制
因为javascript语言特点就是单线程,这就意味着所有的任务都需要排队,只有当前一个任务结束后,才会执行后一个任务,如果前一个任务耗时很长,那有一个任务就得一直等着,于是js的所有任务又分为同步任务、异步任务,同步任务是指:在主线程上排队执行的任务,从上往下执行。异步任务是指:所有的异步任务不会进入主线程,而是会进入任务队列(task queue),只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行;
- 异步执行的运行机制如下
1、所有的同步任务都在主线程执行,形成一个执行栈(execution content stack)。
2、所有的异步任务都在任务队列(task queue)中,只有异步任务有了运行结果,就在 任务队列中放置一个事件,通知主线程可以执行该任务了(任务队列中的事件:除了IO设备的事件以外,还包括用户产生的事件,比如鼠标点击、页面滚动等等)。
3、当执行栈中的同步任务执行完毕后,就会读取 任务队列 中的事件,然后这些 异步任务 进入执行栈中开始执行。
4、主线程会不断读取 任务列表 中的事件,
只要主线程空了,就会读取 任务队列 ,这就是javascript的运行机制,因为这个过程是循环不断的,所以整个的这种运行机制又称为EventLoop(事件循环)。
(二):Promise异步处理
promise主要用于异步任务的操作,解决了代码嵌套的问题,promise中的代码会立即执行;
promise的又三种状态:
- pending (进行中)
- fulfilled(已成功)
- rejected(已失败)
这三种状态只能从pending到fulfilled,或者pending到rejected两种情况,状态的改变是不可逆的,下面看一下这段代码的执行顺序
console.log(1); // 同步
new Promise((resolve, reject) => {
console.log(2); // 这里的代码会立即执行
setTimeout(() => {
console.log(3); // 第一个异步任务
}, 0);
}).then(() => {
console.log(4); // 这里的代码只有当状态改变为fufilled的时候才会执行
}).catch(err => {
// 当状态改变为rejected的时候才会执行
})
setTimeout(() => {
console.log(5); // 第二个异步任务
}, 0);
console.log(6); // 同步
// 1、2、6、3、5
然后在看下一段代码
new Promise((resolve, reject) => {
setTimeout(() => {
console.log(3); // 第一个异步任务
resolve() // 改变promise的状态, 从pending --> fulfilled
}, 0);
}).then(() => {
console.log(4); // 挡状态改变的时候就会立即执行
});
setTimeout(() => {
console.log(5);
}, 0);
// 3、4、5
在来一段
new Promise((resolve, reject) => {
setTimeout(() => {
console.log(3);
}, 0);
resolve()
}).then(() => {
console.log(4);
});
setTimeout(() => {
console.log(5);
}, 0);
// 4、3、5
下面这段代码,有点绕
new Promise((resolve, reject) => {
console.log(0)
setTimeout(() => {
console.log(4);
}, 0);
resolve()
}).then(() => {
console.log(1)
}).then(() => {
setTimeout(() => {
console.log(5)
}, 0)
})
console.log(3)
setTimeout(() => {
console.log(6);
}, 0);
// 0、3、1、4、6、5
promise的缺点:
1、创建promise后会立即执行,不能中途取消;
2、如果不设置回调函数,promise内部抛出的错误,外部无法知晓
3、当处于pending状态的时候,无法得知进展到那个阶段
promise实现的简单原理:
// 第一步:Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。
// 它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
function Promise(task) {
let that = this; // 缓存this
that.status = 'pending'; // 进行中的状态
that.value = undefined; //初始值
that.onResolvedCallbacks = []; // 存放成功后要执行的回调函数的序列
that.RejectedCallbacks = []; // 存放失败后要执行的回调函数的序列
// 该方法是将Promise由pending变成fulfilled
function resolve (value) {
if (that.status == 'pending') {
that.status = 'fulfilled';
that.value = value;
that.onResolvedCallbacks.forEach(cb => cd(that.value))
}
}
// 该方法是将Promise由pending变成rejected
function reject (reason) {
if (that.status == 'pending') {
that.status = 'rejected';
that.value = reason;
that.onRjectedCallbacks.forEach(cb => cd(that.value))
}
}
try {
// 每一个Promise在new一个实例的时候 接受的函数都是立即执行的
task(resolve, reject)
} catch (e) {
reject(e)
}
}
// 第二步 写then方法,接收两个函数onFulfilled onRejected,
// 状态是成功态的时候调用onFulfilled 传入成功后的值,
// 失败态的时候执行onRejected,传入失败的原因,
// pending 状态时将成功和失败后的这两个方法缓存到对应的数组中,当成功或失败后 依次再执行调用
Promise.prototype.then = function(onFulfilled, onRejected) {
let that = this;
if (that.status == 'fulfilled') {
onFulfilled(that.value);
}
if (that.status == 'rejected') {
onRejected(that.value);
}
if (that.status == 'pending') {
that.onResolvedCallbacks.push(onFulfilled);
that.onRjectedCallbacks.push(onRejected);
}
}
(三) async await异步处理
async 函数在function 前面加一个async作为标识,表示为异步函数,在函数体内部需要搭配await来使用
async函数的特点:
1、处理异步函数的语义化强
2、await只能在async中使用
3、async标识的函数返回的是一个Promise对象
4、await语句后面的Promise对象变成rejected状态的时候,那么async函数会中断,后面的程序不会执行
直接上代码:
function f1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('你好')
}, 10)
})
}
async function createAsync(){
await f1();
console.log('222')
}
createAsync()
这段代码只会打印出 你好,222不会被打印出来,因为f1函数返回的promise的状态没有被改变,所以createAsync函数会被中断
function f1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('你好')
resolve() // 改变promise的状态为fulfilled
}, 10)
})
}
async function createAsync(){
await f1();
console.log('222')
}
createAsync()
// 此时打印 你好 222
这里可以看出 await 后面的函数需要返回一个promise对象,并且要改变promise对象的状态才能执行f1函数后面的内容
参数的传递:
function f1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('你好')
resolve('传参')
}, 10)
})
}
async function createAsync(){
const param = await f1();
console.log(param)
}
createAsync()
错误捕获,当f1函数中的promise的状态改为rejected的时候,处理方式:
function f1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('你好')
reject('错误处理, promise的状态修改为rejected')
}, 10)
})
}
async function createAsync(){
try {
const param = await f1();
console.log(param)
} catch(e) {
console.log(e)
} finally {
console.log('5555')
}
}
createAsync()
async函数需要通过try{} catch(e){}的语法来捕获错误
try语法介绍:
1、try语句允许我们定义在执行时进行错误测试的代码块。
2、catch 语句允许我们定义当 try 代码块发生错误时,所执行的代码块。
3、finally 语句在 try 和 catch 之后无论有无异常都会执行。
注意: catch 和 finally 语句都是可选的,但你在使用 try 语句时必须至少使用一个。
欢迎大家评论,如果觉得有用,可以点个赞,感谢大家哈