同步编程

同步编程指的是当一个任务在执行时,另一个任务需要等到之前的任务执行完毕才能继续执行,换句话说,就是一段代码的执行会阻塞另一段代码的执行,这就叫做同步编程。

异步编程

异步编程指的是当一个任务在执行时,另一个任务也可以同时执行,一段代码的执行不会阻塞另一段代码的执行,这种叫做异步。

js为什么需要异步编程

js是单线程的,所以如果js都是同步执行代码的话,就会造成阻塞。因此在js编程中,会大量使用js来进行异步编程。

js异步编程方式

先放结论:

ES6 JS 异步 fetch js异步编程的方法_ES6 JS 异步 fetch

回调函数

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 ,打印结果的顺序就会变化。