JavaScript中async/await 关键字简解

async关键字

js本身是单线程的,使用async 关键字的函数会被标记成异步函数,需要注意的是,这个异步与Java中的线程不同,它需要await 来实现阻塞才能实现伪异步,它不是并发去跑async 函数内部的代码逻辑,而是等待结果的过程中去运行主函数(等待结果是指ajax请求已经发出,但是没接收到结果,代码逻辑已经跑完,且在跑async函数内代码逻辑的时候是同步的),主函数运行结束后再处理异步函数的结果

例如java中代码:

public class test {

    public static void main(String[] args) {
      Thread a=  new Thread(new Runnable() {
        @Override
        public void run() {
            Integer i=0;
            while (i<10000000) {
                i++;   
            }
            System.out.println("a执行完毕");
        }
      });

      Thread b=  new Thread(new Runnable() {
        @Override
        public void run() {
            Integer i=0;
            while (i<1000) {
             i++;   
            }
            System.out.println("b执行完毕");
        }
      });

      a.start();     
      b.start();
      System.out.println("主线程结束");
    }
}

//结果--即使是a先执行,但是b先出结果,说明b的执行不会被a影响
主线程结束
b执行完毕
a执行完毕

js中类似代码:

async function  B1() {
    return new Promise((resolve,reject)=>{
        resolve("return的结果");
        i=0
        while(i<1100000000){
            i++
        }
        console.log("B1--执行");
    })

}
async function  B2() {

    i=0
    while(i<100){
        i++;
    }
    console.log("B2--执行");

}

B1();
B2();
console.log("结束")


//输出结果--无论怎么运行,依旧是这个结果
B1--执行
B2--执行
结束

js是单线程的,不能做到java那样a和b不相互影响,单它允许阻塞当前节点,再等待结果的过程中(等待结果是指ajax请求已经发出,但是没接收到结果,代码逻辑已经跑完,且在跑async函数内代码逻辑的时候已经是同步的),去运行主节点,就到了await关键字的作用了

await 关键字

 

运行线程跳出当前子节点,返回父节点

示例代码:

async function  B1() {
        i=0
        console.log("B1--执行");
         B2(); 
      console.log("B2--执行执行完毕");
      
 }
 

async function  B2() {
    console.log("B2--执行");
}


B1();
//节点B
console.log("主线程---当前是最后打印的吗?");


// 执行结果:依旧是同步的?依旧是同步的

B1--执行
B2--执行
B2--执行执行完毕
主线程---当前是最后打印的吗?

需要改成:

实现伪异步:

async function  B1() {
        i=0
        console.log("B1--执行");
       await  B2(); // 等待B2结果,阻塞当前节点A ,如果B2方法内部没有await 那么会跳转到节点B
       // 节点A
      console.log("B2--执行执行完毕");
      
 }
 

async function  B2() {
    console.log("B2--执行");
}


B1();
//节点B

console.log("主线程---当前是最后打印的吗?");


// 结果:
B1--执行
B2--执行
主线程---当前是最后打印的吗?
B2--执行执行完毕


 

为什么我会说是假异步呢,如果往节点B后面添加给=个循环,那么它将会影响节点A的执行

例如:

async function  B1() {
        i=0
        console.log("B1--执行");
       await  B2(); // 等待B2结果,阻塞当前节点A 
       // 节点A
      console.log("B2--执行执行完毕");
      
 }
 

async function  B2() {
    console.log("B2--执行");
}


B1();
//节点B

console.log("主线程---当前是最后打印的吗?");
i=0
while(i<1000000){ // 当前循环依旧会影响到节点A,当循环结束才会 打印 "B2--执行执行完毕"
    i++;
    console.log(i);
}


运行顺序:

那么我们就能非常确的它代码的执行顺序

例如代码:

async function  B1() {
    // 节点A
    console.log("结束B1")
}

async  function B2(){
    await B1();
    // 节点B
    console.log("结束B2")
}
async  function B3(){
    await B2();
    // 节点C
    console.log("结束B3")
}

// 运行
B3();
// 节点D
console.log("结束")

/**
 * 输出结果:
 *
 * 结束B1
 * 结束
 * 结束B2
 * 结束B3
 *
 */

分析:

说明:await不会马上跳出当前节点,会先判断当前await A()中的A方法中是否还有await,如果有就继续深入,如果没有就跳出到主节点D中。

具体流程:

线程运行到B3()方法,B3方法中有await B2() 会进入B2() 中,而B2中有await B1() ,那么最终会进入进入到B1 ,执行输出 ”结束B1“,之后因为B1内没有了await,那么会跳到主节点节点D中,节点D中执行 ”结束“,之后返回到节点B中执行 ”结束B2“ ,之后跳到节点C中 执行”结束B3“,至此程序结束。

那么对比下面,我们也能猜到下面程序的执行顺序

 

var a1=0;
async function A1(){
    a1++;//3 5
    console.log("A1",a1); // 执行顺序1 // 执行顺序4
}

async function A2(){
    a1++;//2 4
    await A1();
    console.log("A2",a1);  // 执行顺序3 // 执行顺序5
}



async function A4(){
    a1++;//1
    await A2();
    await A2();

    console.log("A4",a1); // 执行顺序6
}


A4()
console.log("主节点",a1); // 执行顺序2


/**

执行结果:
A1 3
主节点 3
A2 3
A1 5
A2 5
A4 5
**/

只需要记得嵌套最里面层的await 等待异步函数的全部执行,执行完毕后才会才会跳到主节点,也就是所js的异步方法执行顺序的可控的,它的异步不是java的线程并发,而是“后续再处理执行结果”,等待主节点完成后再执行await下面的内容,异步函数的顺序也是按事件队列的顺序执行,并不是并发。如果不使用await那么异步函数的执行顺序和没有使用async关键字是一样的,即就是一个普通函数。

 

返回值Promise 

async 标记的函数的返回值都是Promise  类型的

即使没有直接标明是Promise  它依旧会被封装成Promise 

例如:

async function C1(){
    
    i=0;
    while(i<1000000000){
        i++
    }
    console.log("C1");
    return "结果1"
}
async function C2(){
    
    i=0;
    while(i<100000){
        i++
    }
    console.log("C2");
    return "结果2"
}

  function A(){
    C1().then((data)=>{
        i=0;
        while(i<1000000000){ // then的运行不会影响主节点,他是需要在主节点结束后在运行
            i++
        }
        console.log("C1运行结果:"+data);
        i=0;
        
    })
    console.log("C1执行完毕当前依旧会被阻塞");
    C2().then((data)=>{ // then的运行不会影响主节点,他是需要在主节点结束后在运行,但then的运行也不是并发,也是按事件顺序
        i=0;
        while(i<1000){ // 当前处理短,但是也不会优先于C1
            i++
        }
        console.log("C2结果:"+data);
      
    })
    console.log("C2执行完毕当前节点会被阻塞C1,C2的逻辑阻塞,但是不会被then阻塞");
    
}

console.log("主节点不会被阻塞1");
A()
console.log("主节点还是会被阻塞C1,C2的逻辑阻塞,但是不会被then阻塞2");

执行结果是唯一的:


主节点不会被阻塞1
C1
C1执行完毕当前依旧会被阻塞
C2
C2执行完毕当前节点会被阻塞C1,C2的逻辑阻塞,但是不会被then阻塞
主节点还是会被阻塞C1,C2的逻辑阻塞,但是不会被then阻塞2
C1运行结果:结果1
C2结果:结果2

then和await 的区别

它们本质上

 下面举例说明:

console.log("Start");

async function fetchData() {
  try {
    console.log("fetchDat");// 当前还是立刻执行的,还是比console.log("End"); 先执行
    const response = await axios.get('https://api.example.com/data'); // 立即发送请求
    console.log(response.data); //response 有结果才会执行,即最后执行
  } catch (error) {
    console.error(error);
  }
}

fetchData();

console.log("End");

对比then

console.log("Start");

async function fetchData() {
    console.log("fetchData"); // 当前是立刻执行的 console.log("Start"); 结束后执行
    axios.get('https://api.example.com/data')  // 当前是立刻执行的   console.log("fetchData");结束后执行

    .then((data)=>{ // then或者catch里面的内容的是在 console.log("End");结束后,即使主节点结束后再执行的

        console.log("最后执行");
    })
    .catch((err)=>{
        console.log("最后执行");
    })
    console.log("fetchData--edd");  //在  axios.get('https://api.example.com/data')  发送请求结束后执行

}

fetchData();

console.log("End");

then 的内部代码块就相当于await 的下面部分的代码块,本质上没什么区别。

 

async function ad1(){
    try{
     let a=   await ad2()
     console.log("数据1:",a);
    }catch(e){
        console.log("错误:"+e);
    }
   
    
}

async function ad2(){
    return new Promise((resolve,reject)=>{
         // let a=c/0
          console.log("输出");
         let i=0
         while(i<10000){
            i++;
        }
        console.log("数据2");
         resolve(1/1) // 正确结果
        //  reject(new Error("错误"))
    })
  
}

ad1();
console.log("在--数据2--后面");

其实return Promise和直接return正确结果是一致的,本质上async 会对返回值进行封装。

 总之:js的异步函数和普通函数一样会执行,只是执行的结果会在主函数结束后再处理,异步函数并没有并发,依旧是按顺序执行。

 

你可能感兴趣的:(javascript,前端,开发语言)