Skip to content

一文搞懂异步编程回调地狱解决方案-promise,async,await

一.什么是回调地狱

js
setTimeout(function () {
  //第一层
  console.log(111);
  setTimeout(function () {
    //第二程
    console.log(222);
    setTimeout(function () {
      //第三层
      console.log(333);
    }, 1000);
  }, 2000);
}, 3000);

这种回调函数的层层嵌套,就叫做回调地狱。回调地狱会造成代码可复用性不强,可阅读性差,可维护性差,扩展性差等等问题。

二.解决方法

2.1 Promise 语法

  1. promise 对象有三个状态:pending(进行中),fulfilled(已成功),rejected(已失败)

  2. Promise 对象的状态改变, 只有两种可能:

    2.1. 从 pending 变为 fulfilled。此时应该执行 resolve();

    2.2. 从 pending 变为 rejected。此时应该执行 reject();

  3. promise 在创建对象的时候,里面的代码会立即执行

    3.1. promise 创建时,里面的代码还是异步无序操作

    3.2. promise 的原理是,利用 then 方法将异步操作的结果,按照顺序执行,catch 方法用来接收处理失败时相应的数据

  4. promise 解决回调地狱原理:在 then 方法中返回一个 promise 对象

js
//1.封装一个函数 :  根据文件名生成  文件读取的promise
function getPromise(fileName) {
  let p = new Promise((resolve, reject) => {
    //读文件
    fs.readFile(`./data/${fileName}.txt`, "utf-8", (err, data) => {
      if (err == null) {
        //成功
        resolve(data);
      } else {
        //失败
        reject(err);
      }
    });
  });
  return p;
}

//2.解决需求: 要先读a, 读完a后读b, 读完b后读c.

//开始读取a
getPromise("a")
  .then((data) => {
    console.log(data);
    //继续读取b
    return getPromise("b");
  })
  .then((data) => {
    console.log(data);
    //继续读取c
    return getPromise("c");
  })
  .then((data) => {
    console.log(data);
  })
  .catch((err) => {
    //以上三个异步操作,只要有任何一个出错,都会执行err
    console.log(err);
  });
  • 但是如果过多的使用 then 也会照成新的执行流程问题。所以 ES2017 推出了 async/await (异步等待)

2.2 async/await

  • async 语法如下
  1. 函数前面使用 async 修饰
  2. 函数内部,promise 操作使用 await 修饰
java
-   `await` 后面`是promise对象`, 左侧的返回值就是这个promise对象的then方法中的结果
-   `await`必须要写在`async`修饰的函数中,不能单独使用,否则程序会报错
  1. async 函数内部的异常需要通过 try,catch 来捕获
java
const fs = require("fs");

/*
promise实例对象的catch方法 : 用于捕获异步操作的错误信息
*/


//1.封装一个函数 :  根据文件名生成  文件读取的promise
function getPromise(fileName) {
    let p = new Promise((resolve, reject) => {
        //读文件
        fs.readFile(`./data/${fileName}.txt`, 'utf-8', (err, data) => {
            if (err == null) {
                //成功
                resolve(data);
            } else {
                //失败
                reject(err);
            }
        });
    });
    return p;
};

//2.解决需求: 要先读a, 读完a后读b, 读完b后读c.

// async和await异步函数 :  这两个关键字只能用于函数, 所以用的时候一定要放在函数里面用

/*
async关键字:  修饰函数。  表示这个函数内部有异步操作。
await关键字:  等待异步执行完毕。
    (1)await只能用于被async修饰的函数中。
        只有当await后面的异步操作执行完毕后,才会继续执行后面代码
    (2)await 后面 只能是promise对象
*/

const readFile = async () => {

    let data1 = await getPromise('a')
    console.log(data1)

    let data2 = await getPromise('b')
    console.log(data2)

    //async异步函数的错误信息要用try-catch来捕捉
    try {
        let data3 = await getPromise('c')
        console.log(data3)
    } catch (err) {
        console.log(err)

    }
}

readFile()