单线程、主线程与任务队列

我们知道,JavaScript是单线程的。也就是它只能一个线程去执行任务。什么是单线程呢

举个栗子:你要做家务,要拖地、还要煮饭、还要洗衣服,那么你只能一件一件事情完成,在拖完地后煮饭,之后再洗衣服,当然你也可以叫你旁边的人帮你洗衣服,然后你拖地,这样子的话,两个人做就算做双线程了,多个人的话就是多线程了,而现在是js在执行任务,你就不能去叫别人来帮忙了,那么你只能一件一件做了,这便是单线程。

接下来来理解一下异步是怎么一回事。

还是拿上面那件事,假设现在洗衣服可以通过异步完成——洗衣机洗衣服、煮饭可以通过电饭煲煮,那么我们就可以这样做——把衣服放到洗衣机、米放到电饭煲、然后执行拖地,拖完地了,可能衣服这时候已经晾好了,那么我们就可以去晾个衣服、晾完衣服饭也煮好了,那么我们就可以去吃饭了。

这便是js的执行过程,主线程执行同步任务——拖地,任务队列存放异步任务——洗衣机洗好了衣服、电饭煲煮好了饭。

那么我们就可以理解,什么是主线程——执行同步任务,什么是任务队列——存放异步任务的结果,js首先执行主线程里面的任务,当主线程中的同步任务执行完后,就开始从任务队列读取,哪些异步任务执行好了,执行好的就拿过来执行栈(主线程)执行,然后不断循环执行。

主线程与任务队列

异步编程

了解了异步的基础——主线程与任务队列后,我们来进入下个知识点——异步编程

博客讲解了怎么得到函数内部异步操作的结果,以及因此而产生的回调地狱问题,以及如何解决回调地狱——Promise

注意:Promise是进入async/await学习必须理解的知识点

async/await

首先了解async和await的书写规范

async必须写在function前面,除了这里,在其他地方声明都会报错

1
var a = async function() {}

await必须在async声明的函数内部使用,注意:不能跨过该函数作用域使用

1
2
3
4
5
//可以
let x = 3
var a = async function() {
let y = await x
}
1
2
3
4
5
6
7
8
//不可以
let x = 3
var a = async function() {
var b = function() {
let y = await x
}
}
a()

await作用:等待一个Promise的异步返回才会执行下面的代码(await是在等待一个Promise的异步返回)

使用

首先声明两个异步函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let wait1 = function() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我延迟了一秒')
// reject('我延迟一秒出错了')
}, 1000)
})
}

let wait2 = function() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我延迟了两秒')
}, 2000)
})
}

我们想要在执行完wait1后执行wait2,在传统的Promise执行是这样的

1
2
3
4
5
6
7
8
9
10
11
12
//传统Promise
wait1()
.then((res,rej) => {
console.log(res)
return wait2()
})
.then((res,rej) => {
console.log(res)
})
.catch((err) => {
console.log(err)
})

async/await执行

1
2
3
4
5
6
7
8
9
10
11
// async
let a = async function(){
let b = await wait1()
let c = await wait2()
console.log(c)
console.log(b)
console.log('哈哈哈')
}
a().catch(err => {
console.log(err)
})

有没觉得简单了许多,async的出现主要就是为了解决Promise造成的冗余的长长的链式代码,这样做也可以使代码的语义化更加清楚,这里有一篇博客推荐阅读,详细的介绍了async