首先理清几个概念
- JS是单线程语言,JS诞生的用途是为了操作DOM以及与用户的交互,因此只能是单线程,多个线程同时操作一个dom时不合理的
- 同步和异步:能直接拿到结果的是同步,不能直接拿到结果的就是异步
- 同步
function second(){
console.log('2')
}
function first(){
console.log('1')
second()
console.log('3')
}
first()
/*
"1"
"2"
"3"
*/
- 异步
const getList = () => {
setTimeout(() => {
console.log(2);
}, 2000);
};
console.log(1);
getList();
console.log(3);
//1 3 2
- 调用栈:js执行代码时会将函数进行以队列(后进先出)的形式压栈,执行完毕就弹栈
- web worker标准允许js可以创建多个线程,但是子线程完全受主线程控制,且不得操作dom,不影响js单线程的本质
- 消息队列:同步执行代码的时候,会有一个“执行栈”,开启多线程时,浏览器还会维护一个消息列表,执行栈执行主线程,消息列表是副线程(异步)的集合
- 宏任务、微任务:宏任务是同步流程中出现的的代码,setTimeout、setInterval、setImmediate、I/O等。
代表性的微任务如promise.then catch finally、process.nextTick(node环境)\ MutationObserver等
- 事件轮询(EventLoop):主线程优先执行,主线程执行完后执行消息列表(副线程),这都是在执行宏任务。当碰到有微任务,执行完微任务才会执行下一个宏任务。
promise
在async和await之前,还需要了解Generator函数
Generator函数最大特点是可以交出函数的执行权(暂停执行)
next()一下走一步
function* doWhat(){
yield '吃饭'
return '睡觉'
}
let man=doWhat()
console.log(man.next())
console.log(man.next())
/*
{
done: false,
value: "吃饭"
}
{
done: true,
value: "睡觉"
}
*/
function* xxx和function *xxx一样- yield将函数截成两个状态
- Generator不会自己执行,而是返回一个遍历器对象
- 遍历器对象通过.next()方法调用各个状态
Generator可用于消息传递
function *x() {
let x = yield '我启动了!'
let y = yield (x + 3)
let z = yield (y * 3)
return (x * 2)
}
let y = x()
console.log(y.next(1)) // {value: "我启动了!", done: false}
console.log(y.next(2)) // {value: 5, done: false}
console.log(y.next(100)) // {value: 300, done: false}
console.log(y.next(1000)) // {value: 4, done: true}
async、await就是Genarator的语法糖
通过Generator函数加自动执行器实现,于是不需要一直next()
function f() {
return new Promise(resolve =>{
resolve('hhh')
})
}
async function doSomething1(){
let x = await f()
console.log(x)
}
doSomething1()
//hhh
- async 修饰符表示这个函数是异步函数
- await 是个运算符,阻塞后面代码
- await 如果等到的Promise对象就得到其resolve值
async function doSomething1(){
let x = await 'hhh'
return x
}
console.log(doSomething1())
doSomething1().then(res => {
console.log(res)
})
//打印结果:
//Promise {<pending>}
//hhh
- async 返回一个Promise对象,async修饰的函数内部返回的值,会成为then中回调方法的参数
- await如果等到的不是Promise对象,就得到一个表达式的运算结果