回调函数

  • 概念:一个函数作为另一个函数的参数;先执行一个函数,把他的执行结果作为另一个函数的参数;

  • 弊端:会产生回调地狱。(回调函数的层层嵌套 例如:京东首页的tab栏,家用电器里面有电视,电视里面有各种牌子,不同牌子又有不同的配色等等,类似这样下一级的参数为上一级的返回值的)

Promise

代码可见:github

  • 概念:

    1. Promise 是异步编程的一种解决方案,比传统的回调函数和事件更合理、更强大
    2. ES6的Promise是一个构造函数, 用来生成Promise实例, Promise实例是异步操作管理者
    3. Promise代表了未来某个将要发生的事件(通常是一个异步操作) 有了Promise对象
    4. 可以将异步操作以同步的流程表达出来, 避免了层层嵌套的回调函数(回调地狱)
    5. Promise本身还是在使用回调函数(只不过比回调函数多了一种状态管理)
    6. promise三个状态:
      • 初始化状态 pending
      • 成功状态 fullfilled
      • 失败状态 rejected
  • 如何使用

    1. 因为promise是构造方法,就是一个类,所以说使用的时候需要进行实例化

    2. 使用new进行实例化一个Promise 内置有三种状态

    3. promise内置一个参数 参数是回调函数 回调函数中有两个参数 其实都是函数,这两个参数代表着成功状态和失败状态

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      let promise = new Promise((resolve,reject)=>{
      // 回调函数中有两个参数,这两个参数都是函数,
      console.log(typeof resolve); //function
      console.log(typeof reject); //function
      // 初始化状态其实就是许诺状态
      let num = 10;
      if(num>9){
      resolve("成功");
      }else{
      reject("失败")
      }
      })

      以上是我们创建的promise

      但是创建之后如何使用 现在的promise对象和没写一样 一点作用都没有

      使用promise对象进行触发 promise对象中的then方法

      then中内置一个或者两个参数 第一个参数是我们定义成功状态的执行方法

      第二个参数是我们定义的失败状态的执行方法 也就是说两个参数 都是函数

      • 传统写法
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      // Promise内置一个参数  参数是回调函数  回调内置两个参数
      // 第一个参数是成功状态 第二个参数是失败状态
      // 这两个参数其实都是函数 只不过还没有定义
      let promise = new Promise((resolve,reject)=>{
      // 初始化一个值
      let num = 10;
      // 判定
      if (num > 90){
      resolve('成功');
      }else{
      reject('失败');
      }
      });
      // ps:可以理解为上面的resolve("成功")和reject("失败")为函数的调用,括号里传的为实参;而promise.then里面的两个参数为函数的声明,data和err为形参;
      promise.then(data=>{
      console.log('执行已经' + data);
      },err=>{
      console.log('执行出现了' + err);
      });
      • 链式操作(现写法)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      let promise = new Promise((resolve,reject)=>{
      // 初始化一个值
      let num = 10;
      console.log(aaa);
      // 判定
      if (num > 90){
      resolve('成功');
      }else{
      reject('失败');
      }
      });
      // 我们一般不会在then方法中同时传递两个参数(传统写法),因为如果说传递两个参数 那么不会进行异常处理
      // 所以说 失败的状态 我们一般情况下 使用catch状态进行触发 这样经过了异常处理
      // 使用then方法触发成功状态 使用catch方法触发失败状态
      // 但是注意 catch不是promise的方法 而是then的返回值的方法
      // 也就是说 promise对象使用then触发之后 then方法的返回值也是一个promise
      //常规写法
      let p =promise.then(data=>{
      console.log("当前状态为:"+data)
      })
      p.catch(err=>{
      console.log("当前状态为:"+err)
      })
      // 链式操作
      promise.then(data=>{
      console.log(data);
      }).catch(err=>{
      console.log(err);
      })
      • 链式操作
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      let promise = new Promise((resolve,reject)=>{
      if (true){
      resolve('一级栏目');
      }else{
      reject('没出来');
      }
      })

      // 第一个promise中的无论是成功状态还是失败装填的返回值
      // 都作为链式操作中第二个promise的resolve(成功)状态
      // let p = promise.then(data=>{
      // console.log(data);
      // return data; //记得return出去
      // }).catch(err=>{
      // console.log(err);
      // return err; //记得return出去
      // })

      // p.then(data1=>{
      // console.log("根据" + data1 + "获取二级栏目");
      // })

      let p =promise.then(data=>{
      console.log(data);
      return data;//记得return出去
      }).then(data1=>{
      console.log("根据"+data1+"获取二级栏目")
      return "根据"+data1+"获取二级栏目";//记得return出去
      }).then(data2=>{
      console.log(data2+"获取三级栏目")
      return data3;//记得return出去
      })
      /*
      一级栏目
      根据一级栏目获取二级栏目
      根据一级栏目获取二级栏目获取三级栏目
      */
      • Promise.all()
        1. all方法是处理Promise的方法 他是Promise中的一个静态方法 static
        2. 也就是说 我们使用的时候 不需要实例化 我们直接使用Promise.all方法就可以
        3. 它主要的作用是执行所有的promise
        4. 内置一个参数 参数是一个数组
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      let p1 = new Promise((resolve,reject)=>{
      setTimeout(()=>{
      resolve('2s钟之后执行p1');
      },2000)
      })

      let p2 = new Promise((resolve,reject)=>{
      setTimeout(()=>{
      resolve('3s钟之后执行p2');
      },3000)
      })

      let p3 = new Promise((resolve,reject)=>{
      setTimeout(()=>{
      resolve('1s钟之后执行p3');
      },1000)
      })

      // all方法是执行所有的Promise
      // 内置一个参数 参数是一个数组
      Promise.all([p1,p2,p3]).then(data=>console.log(data));//['2s钟之后执行p1', '3s钟之后执行p2', '1s钟之后执行p3']
      • Promise.race()
        1. all方法是处理Promise的方法 他是Promise中的一个静态方法 static
        2. 也就是说 我们使用的时候 不需要实例化 我们直接使用Promise.race方法就可以
        3. 他只执行最快的那个promise
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      let p1 = new Promise((resolve,reject)=>{
      setTimeout(()=>{
      resolve('2s钟之后执行p1');
      },2000)
      })

      let p2 = new Promise((resolve,reject)=>{
      setTimeout(()=>{
      resolve('3s钟之后执行p2');
      },3000)
      })

      let p3 = new Promise((resolve,reject)=>{
      setTimeout(()=>{
      resolve('1s钟之后执行p3');
      },1000)
      })

      //race方法是执行最快的Promise
      Promise.race([p1,p2,p3]).then(data=>console.log(data));//1s钟之后执行p3

async

  • 概念:

    1. 真正意义上去解决异步回调的问题,同步流程表达异步操作
    2. 本质上async 是Promise的语法糖
  • 特点:

    1. async函数是一个异步函数 但是主要的作用是处理函数内部的异步问题
    2. 将函数内部的异步代码转化为同步代码 再执行
    3. 在函数中 如果说程序碰到await 程序会陷入阻塞状态
    4. 也就是说 在async函数中有await关键字 这个关键字只能出现在async函数中
    5. 一直到程序执行完成await后面的表达式 程序才会向下进行
  • async函数主要有三点

    1. 函数必须使用async进行修饰

    2. 函数的返回值是一个promise

    3. await将异步代码转化为同步代码

    4. await可以直接获取Promise中的resolve状态 并且是异常处理过后的

      1
      2
      3
      4
      5
      6
      7
      8
      async function fn(){
      let v1 = await "这是第一行代码";
      let v2 = await "这是第二行代码";
      console.log(v1);
      console.log(v2);
      }
      fn(); //这是第一行代码
      //这是第二行代码

      案例:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
         function fn1() {
      return new Promise((resolve, reject) => {
      setTimeout(() => {
      resolve('2s钟之后执行p1');
      }, 2000)
      })
      }

      function fn2() {
      return new Promise((resolve, reject) => {
      setTimeout(() => {
      resolve('3s钟之后执行p2');
      }, 3000)
      })
      }

      function fn3() {
      return new Promise((resolve, reject) => {
      setTimeout(() => {
      resolve('1s钟之后执行p3');
      }, 1000)
      })
      }
      // 定义async函数
      async function fn() {
      let v1 = await fn1();
      console.log(v1)
      let v2 = await fn2();
      console.log(v2)
      let v3 = await fn3();
      console.log(v3)
      }
      fn();
      /*
      2s钟之后执行p1
      3s钟之后执行p2
      1s钟之后执行p3
      */

      案例传参:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      const connect = require('./lib/connect');

      function fn1(){
      return new Promise((resolve,reject)=>{
      connect.query('select * from category_first',(err,data)=>{
      let result = data[1].first_id;
      resolve(result);
      })
      })
      }

      function fn2(v){
      return new Promise((resolve,reject)=>{
      connect.query('select * from category_second where first_id = ?',v,(err,data)=>{
      let result = data[0].second_id;
      resolve(result);
      })
      })
      }

      function fn3(v){
      return new Promise((resolve,reject)=>{
      connect.query('select * from category_thired where second_id = ?',v,(err,data)=>{
      let result = data[0].thired_id;
      resolve(result);
      })
      })
      }

      function fn4(v){
      return new Promise((resolve,reject)=>{
      connect.query('select goods_name,goods_id from goods_list where thired_id = ?',v,(err,data)=>{
      resolve(data);
      })
      })
      }

      async function fn(){
      let v1 = await fn1();
      let v2 = await fn2(v1);
      let v3 = await fn3(v2);
      let v4 = await fn4(v3);
      console.log(v4);
      }
      fn();
  • 后续补充

  1. async函数返一个promise对象,当函数执行的时候,一旦遇到await就会先返回,等到触发的异步任务完成,再接着执行后面的语句;(为了验证,看如下代码)

    1
    2
    3
    4
    5
    6
    7
    8
    9
        async function test(){
    await setTimeout(() => {
    console.log("我是不是先执行");
    }, 1000);
    console.log("我什么时候执行")
    }
    test();
    // 我什么时候执行
    //(1s后)我是不是最开始执行

    我们发现,并非我们预想的执行顺序,先打印‘我是不是最开始执行’,而是从下面先开始执行了?其实,这里是有条件的:

    • 如果await后面跟的是promise对象,会将promise异步操作转为同步等待promise的resolve/reject返回结果,再接着执行函数体后面的语句

      (继续测试如下):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        async function test() {
    await new Promise((resolve, reject) => {
    setTimeout(() => {
    console.log("我是不是先执行");
    }, 1000);
    })
    console.log("我什么时候执行")
    }
    test();
    // (1s后) 我是不是最开始执行

    我们发现,这跟预期的还是不一样。最下面的 console.log(‘我什么时候执行’)没执行。

    原来:

    • 如果await的是一个promise对象,那么要等待这个对象解析完成;
    • 如果没有resolve/reject,那么后面的内容就不再执行;

    我们再修改,测试如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
        async function test() {
    await new Promise((resolve, reject) => {
    setTimeout(() => {
    resolve();
    console.log("我是不是先执行");
    }, 1000);
    })
    console.log("我什么时候执行")
    }
    test();
    // (1s后) 我是不是最开始执行
    // (1s后,和上面同时) 我什么时候执行
  • 小结论

所以我们可以得出一个结论

  1. await后面如果不是一个promise对象,那么它就按正常的js顺序执行,先执行同步代码,当主线程空闲了,再去执行异步队列的任务;

  2. await后面如果是promise对象那么要等待这个对象解析完成,如果没有resolve或者reject,那么后面的内容就不会执行;如果有resolve或者reject,那么后面的内容正常执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    async function test() {
    console.log("async1");
    await new Promise((resolve, reject) => {
    resolve()
    });
    console.log('async2');
    };
    //等价于
    async function test() {
    console.log("async1");
    await new Promise((resolve, reject) => {
    resolve()
    }).then(() => console.log('async2'));
    };

  • await的返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    async function test() {
let result = await new Promise((resolve, reject) => {
console.log("await1")
setTimeout(() => {
resolve("pkpkq");
}, 1000);
})
console.log("await2",result);
return result;
}
test().then((data)=>{
console.log(data);
})
//await1
//(1s后)await2 pkpkq
//(1s后)pkpkq

我们发现:

  • await的返回值就是Promise的resolve/reject返回结果,然后通过async函数return出来,相当于Promise.resolve(“返回结果”);

参考文献

异步回调中Async Await和Promise区别