什么是异步
我们都知道代码执行时,v8把这段代码先编译再运行,此时cpu就会接受一个指令来执行上下文环境,这时候用的时间就叫做进程。执行指令的时间就是一个线程,所以一个进程可以有多个线程。但是我们的v8编译器跟别的编程语言就有点不一样来看下面的代码。
ini
let a=1
setTimeout(()=>{
a=2
},1000)
console.log(a);
如果按照正常的编译顺序这时候输出打印的a就是2,但是我们可以看到这时的结果是1。很显然,v8是直接先执行了第一行与第五行的代码把中间这个定时器挂起了。
那这是为什么呢?
js默认是单线程运行的(v8默认只会开一个主线程来跑js代码)
- 因为js设计之处是浏览器的脚本语言,设计成单线程可以节约用户的设备性能
- js代码中存在耗时执行的,会被v8挂起,先执行不耗时的(同步代码)
这时候这种编程方式我们就叫作异步
如何解决异步
1.回调函数(初级)
来看这段代码,首先肯定就是先执行不消耗时间的指令所以先输出打印baz,a再是其他另外两个,但是我有没有一种方法可以foo先执行再是bar最后baz呢?
javascript
let a=1
function foo(){
setTimeout(()=>{
a=2
console.log('foo',a);
},1000)
}
function bar(){
setTimeout(()=>{
a=3
console.log('bar',a);
},2000)
}
function baz(){
console.log('baz',a);
}
foo()
这时候我们就可以用到回调函数,把baz放进bar里面,把bar放进foo里面,形成一个嵌套。
scss
let a=1
function foo(){
setTimeout(()=>{
a=2
bar()
console.log('foo',a);
},1000)
}
function bar(){
setTimeout(()=>{
a=3
baz()
console.log('bar',a);
},2000)
}
function baz(){
console.log('baz',a);
}
foo()
但是如果代码这样写的话,代码量大的话,就容易嵌套过深,形成回调地狱,我们维护代码时难以维护,一个函数有问题,一整个代码运行不了。所以这种方法虽然可行但是还是不够完美。
2.promise解决异步(高级)
我们用promise来以一个更加优雅的方式来解决这个问题,来看代码
javascript
let a=1
function foo(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
a=2
console.log('foo',a);
resolve()
},2000)
})
}
function bar(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
a=3
console.log('bar',a);
resolve()
},1000)
})
}
function baz(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
a=4
console.log('baz',a);
resolve()
},500)
})
}
foo().then(()=>{
return bar()
})
.then(()=>{
return baz()
})
在三个函数加入promise Promise 有三种状态:
- pending(进行中) - 初始状态
- fulfilled(已成功) - 操作成功完成
- rejected(已失败) - 操作失败、
Promise 创建后会立即执行 这时候每个promise对象的状态会被修改为成功,而.then顾名思义就是下一个,执行完函数里的再执行.then。而then源码也默认返回了一个promise对象,状态继承了前面的foo。return 让 then 返回的promise状态根据bar返回的promise状态改变
3.Async/Await(进阶)
这里这样写虽然和promise差不多区别就在于最后把then换成了async,await.
-
函数前面加一个async等同于函数内部返回一个promise实例对象
-
await 必须跟async 配合使用,并且await后面如果不接一个promise对象,await无法约束它
-
await fn() 把 fn() 当成同步看待
javascript
let a=1
function foo(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
a=2
console.log('foo',a);
resolve()
},2000)
})
}
function bar(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
a=3
console.log('bar',a);
resolve()
},1000)
})
}
function baz(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
a=4
console.log('baz',a);
resolve()
},500)
})
}
async function fn(){
await foo()
await bar()
baz()
}
fn()
总结:异步编程的"思想升华"
JavaScript的异步演进,就像人类处理多任务的智慧成长:
- 原始时代(回调) :一件事一件事做,记在脑子里(嵌套)
- 农业时代(Promise) :用记事本记下待办事项(任务队列)
- 信息时代(Async/Await) :用智能助手管理所有任务(自动化调度)
记住这三个核心点:
- JavaScript是单线程,但不代表它慢 ------ Event Loop让它高效处理并发
- Promise不是魔法,是一种状态管理机制 ------ 三种状态,两种结果
- Async/Await是语法糖,不是新东西 ------ 底层依然是Promise