同步编程
同步编程指的是当一个任务在执行时,另一个任务需要等到之前的任务执行完毕才能继续执行,换句话说,就是一段代码的执行会阻塞另一段代码的执行,这就叫做同步编程。
异步编程
异步编程指的是当一个任务在执行时,另一个任务也可以同时执行,一段代码的执行不会阻塞另一段代码的执行,这种叫做异步。
js为什么需要异步编程
js是单线程的,所以如果js都是同步执行代码的话,就会造成阻塞。因此在js编程中,会大量使用js来进行异步编程。
js异步编程方式
先放结论:
回调函数
fs.readFile(A,'utf-8',function(err,data){
fs.readFile(B,'utf-8',function(err,data){
fs.readFile(C,'utf-8',function(err,data){
...
}
}
}
这种就是采用回调函数的方式来实现异步,显然这种代码的可阅读性很差,而且后期可维护性也很差,一旦层级变多就会陷入回调地狱。所以我们接下来开始讲解为了解决这种问题的Promise。
Promise
先来看一下针对上述场景的promise书写方式:
function read(url){
return new Promise((resolve,reject) => {
fs.readFile(url,'utf8',(err,data) => {
if(err) reject(err);
resolve(data);
})
})
}
read(A).then(data => {
return read(B);
}).then(data => {
return read(C);
}).then(data => {
return read(D);
}).catch(reason => {
console.log(reason);
});
Generator
Generator函数是ES6提供的一种异步编程解决方案,形式上也是一个普通函数,但有几个显著的特征:
- function关键字与函数名之间有一个星号 “*” (推荐紧挨着function关键字)
- 函数体内使用 yield 表达式,定义不同的内部状态 (可以有多个yield)
- 直接调用 Generator函数并不会执行,也不会返回运行结果,而是返回一个遍历器对象(Iterator Object)
- 依次调用遍历器对象的next方法,遍历 Generator函数内部的每一个状态
代码如下:
function* gen(){
let a = yield 1;
console.log(a);
let b = yield 2;
console.log(b);
let c = yield 3;
console.log(c);
}
let t = gen();
t.next(1); //undefined,因为第一次只负责初始化生成器
t.next(2); //2
t.next(3); //3
async/await
async/await是目前主流的异步编程方式,可以很好的解决回调地狱问题,下面来看一下代码
function testWait() {
return new Promise((resolve,reject)=>{
setTimeout(function(){
console.log("testWait");
resolve();
}, 1000);
})
}
async function testAwaitUse(){
await testWait()
console.log("hello");
return 123;
// 输出顺序:testWait,hello
// 第十行如果不使用await输出顺序:hello , testWait
}
console.log(testAwaitUse());
执行上面的代码,从结果中可以看出,在正常的执行顺序下,testWait 这个函数由于使用的是 setTimeout 的定时器,回调会在一秒之后执行,但是由于执行到这里采用了 await 关键词,testAwaitUse 函数在执行的过程中需要等待 testWait 函数执行完成之后,再执行打印 hello 的操作。但是如果去掉 await ,打印结果的顺序就会变化。