【JavaScript】Promise 语法

前人栽树

许大仙 aexiar.github.io

前言

函数对象和实例对象

  • 函数对象:将函数作为对象使用,简称为函数对象。
  • 实例对象:new 构造函数或类产生的对象,称为实例对象。

示例:

<template>
</template>

<script>
export default {
  name: "promise-01",
  data() {
    return {
    }
  },
  created() {
    // 此时 的 Person 就是函数对象,其实就和 ES6 中的静态属性和静态方法原理上是一样的。
    this.Person.country = '中国';
    console.log(this.Person.country); // 中国

    // 通过 new 构造函数产生的对象 p,称为实例对象
    const p = new this.Person('许大仙', 18);
    console.log(p); // Person {name: '许大仙', age: 18}
  },
  methods: {
    Person(name, age) {
      this.name = name;
      this.age = age;
    }
  }
}
</script>

<style scoped>

</style>

image.png

回调函数

回调函数的定义

  • 如果将函数 A 作为参数传递给函数 B 时,我们就称函数 A 为 回调函数 。换言之,如果一个函数定义了,我们没有调用,但是最终却执行了,那么这个函数就可以称为回调函数。

  • 回调函数常用于 JS 中的事件、定时器、数组等。

  • 示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>

    <button id="btn">点我</button>

    <script>
        // 如果将函数 A 作为参数传递给函数 B 时,我们就称函数 A 为 `回调函数` 。换言之,如果一个函数定义了,我们没有调用,但是最终却执行了,那么这个函数就可以称为回调函数。
        let btn = document.querySelector('#btn');
        btn.addEventListener('click', function () {
            // 回调函数
            console.log('大胆,谁让你点我的~');
        });
    </script>
</body>
</html>
  • 示例:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>

    <button id="btn">点我</button>

    <script>
        // 如果将函数 A 作为参数传递给函数 B 时,我们就称函数 A 为 `回调函数` 。换言之,如果一个函数定义了,我们没有调用,但是最终却执行了,那么这个函数就可以称为回调函数。
        let btn = document.querySelector('#btn');
        // 这边很多人会有疑惑,为什么直接写 fn ,而不是 fn() ?
        // 如果写 fn() ,那么就会立即执行函数,其次,addEventListener 函数的定义就像下面的伪代码:
        // function addEventListener(event,callback){
        //          callback();
        // }
        btn.addEventListener('click', fn);

        function fn() {
            // 回调函数
            console.log('大胆,谁让你点我的~');
        }
    </script>
</body>
</html>

同步的回调函数

  • 同步的回调函数,会立即在主线程上执行,不会放入到回调队列中。

  • 例子:数组遍历相关的回调函数、Promise 的 executor 函数。

  • 示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        let arr = [1, 3, 5, 7, 9];
        arr.forEach(value => {
            // 同步回调函数
            console.log(value);
        });

        console.log('主线程的代码');
    </script>
</body>
</html>

image.png

异步的回调函数

  • 异步的回调函数,不会立即执行,会放入回调队列中再执行。

  • 例子:定时器回调、ajax 回调、Promise 成功或失败的回调。

  • 示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        setTimeout(() => {
            // 异步回调函数
            console.log('定时器执行了');
        }, 0);

        console.log('主线程的代码');
    </script>
</body>
</html>

image.png

JavaScript 的异常处理

  • JavaScript 中的异常(错误)类型:

    • Error:所有异常(错误的父类型。
    • ReferenceError:引用的变量不存在。
    • TypeError:数据类型不正确的错误。
    • RangeError:数据值不在其所允许的范围内。
    • SyntaxError:语法错误。
  • JavaScript 中的异常处理:

    • 捕获异常:try … catch。
    • 抛出异常:throw error。
  • error 对象的结构:

    • message 属性:错误相关信息。
    • stack 属性:记录信息。
  • 示例:

<template>
</template>

<script>
export default {
  name: "promise-01",
  data() {
    return {
    }
  },
  created() {
    // 捕获异常
    try {
      let result = this.divide(10, 0);
      console.log(result);
    } catch (e) {
      console.error(e);
    }
  },
  methods: {
     divide(num1, num2) {
      if (num2 === 0) {
        // 抛出自定义异常
        throw new Error('被除数不能为 0 ');
      }
      return num1 / num2;
    },
  }
}
</script>

<style scoped>

</style>

image.png

异步代码产生的问题

  • 回调地狱

image.png

Promise 概述

  • Promise 的理解
    • 抽象表达:Promise 是 JavaScript 进行异步编程的新方案(旧方案是纯回调函数,纯回调函数会产生回调地狱的问题)。
    • 具体表达:
      • 从语法上讲,Promise 是一个构造函数。
      • 从功能上讲,Promise 对象是用来封装一个异步操作并可以获取其结果。
  • Promise 的状态
    • ① pending(初始化状态) 变为 fulfilled(成功状态) 。
    • ② pending(初始化状态) 变为 rejected(失败状态) 。
  • Promise 是一个类(构造函数),英文翻译承诺、许诺。Promise 是 ES6+ 中引入的,用于解决回调地狱和异步操作的方案。
  • Promise 通过提供一种结构化的方式来处理这些问题。它可以被看作是一个承诺,代表着一个异步操作的最终结果(可以是成功的值或失败的原因)。

注意:

  • Promise 只有这两种状态,且一个 Promise 对象只能被改变一次。
  • 无论变为成功或失败,都会有一个结果数据。
  • 成功的结果数据一般称为 value ,失败的结果数据一般称为 reason (行业规范)。
  • 示例
created() {
/*
* ① Promise 不是一个回调函数,是一个内置的构造函数,是程序员自己 new 调用的。
* ② new Promise((resolve, reject) => {}) 的时候要传入一个回调函数,这个回调函数是同步的,会立即在主线程上执行。这个回调函数称为 executor 函数。
* ③ 每一个 Promise 实例都有 3 种状态,分别为初始化(pending)、成功(fulfilled)和失败(rejected)。
* ④ 每一个 Promise 实例在刚被 new 出来的那一刻的状态都是初始化(pending)。
* ⑤ executor 函数会接收到 2 个函数,它们都是函数,分别用形参:resolve 和 reject 接收。
*    1)调用 resolve 会让 Promise 实例的状态变为成功(fulfilled),同时可以指定成功的 value。
*    2)调用 reject 会让 Promise 实例的状态变为失败(rejected),同时可以指定失败的 reason 。
*/
    // 创建一个 Promise 实例对象
    const promise = new Promise((resolve, reject) => {
      console.log('11');
      resolve(11);
    });
    console.log(promise);
  },

image.png

Promise 基本使用

Promise 的重要语法

new Promise(executor)
Promise.prototype.then

基本编码流程

  • 创建 Promise 的实例对象(pending状态),传入 executor 函数。
  • 在 executor 中启动异步任务(定时器、Ajax 请求)。
  • 根据异步任务的结果,做不同的结果:
    • 如果异步任务成功了,就调用 resolve(value) ,将 Promise 实例的状态变为成功(fulfilled),同时指定成功的 value 。
    • 如果异步任务失败了,就调用 reject(reason),将 Promise 实例的状态变为失败(rejected),同时指定失败的 reason 。
  • 通过 then 方法为 Promise 实例指定成功、失败的回调函数,来获取成功的 value 和失败的 reason 。

注意:then 方法里面指定的成功和失败的回调函数,都是异步的回调函数。

关于状态的注意点

  • 三个状态:
    • pending:未确定的 — 初始化状态。
    • fulfilled:成功的 — 调用 resolve() 后的状态。
    • rejected:失败的 — 调用 reject() 后的状态。
  • 两种状态改变:
  • 状态只能修改一次!!
  • 一个 Promise 实例指定了多个成功/失败的回调函数,都会被调用。

示例代码:

/*
  * 1. 重要语法
  *   ① new Promise(executor)构造函数
  *   ② Promise.prototype.then 方法
  * 2. 基本编码流程
  *   ① 创建 Promise 的实例对象(pending状态),传入 executor 函数。
  *   ② 在 executor 中启动异步任务(定时器、Ajax 请求)。
  *   ③ 根据异步任务的结果,做不同的结果:
  *      1)如果异步任务成功了,就调用 resolve(value) ,将 Promise 实例的状态变为成功(fulfilled),同时指定成功的 value 。
  *      2)如果异步任务失败了,就调用 reject(reason),将 Promise 实例的状态变为失败(rejected),同时指定失败的 reason 。
  *   ④ 通过 then 方法为 Promise 实例指定成功、失败的回调函数,来获取成功的 value 和失败的 reason 。
  *      注意:then 方法里面指定的成功和失败的回调函数,都是异步的回调函数。
  * 3. 关于状态的注意点
  *   ① 三个状态:
  *       pending:未确定的 --- 初始化状态
  *       fulfilled:成功的 --- 调用 resolve() 后的状态
  *       rejected:失败的 --- 调用 reject() 后的状态
  *   ② 两种状态改变:
  *       pending --> fulfilled
  *       pending --> rejected
  *   ③ 状态只能修改一次!!
  *   ④ 一个 Promise 实例指定了多个成功/失败的回调函数,都会调用。
  */
  // 创建一个 Promise 实例对象
  const promise = new Promise((resolve, reject) => {
      // 启动异步任务
      setTimeout(() => {
          let flag = true;
          let data = '用户数据';
          if (flag) {
              // 异步任务成功,就调用 resolve(value)
              resolve(data);
          } else {
              // 异步任务失败,就调用 reject(reason)
              reject(data);
          }
      }, 1000);
  });

  promise.then(value => {
      // 成功的回调--异步
      console.log('成功的回调1', value);
  }, reason => {
      // 失败的回调--异步
      console.error('失败的回调1', reason);
  });

  promise.then(value => {
      // 成功的回调--异步
      console.log('成功的回调2', value);
  }, reason => {
      // 失败的回调--异步
      console.error('失败的回调2', reason);
  });

image.png

Promise API

Promise 的构造函数

  • 语法:
new Promise(executor){}
  • executor 函数是同步执行的,(resolve,reject) => {}
  • resolve 函数:调用 resolve 会将 Promise 的实例内部的状态改为成功(fulfilled)。
  • reject 函数:调用 rejected 会将 Promise 的实例内部的状态改为失败(rejected)。

注意:executor 函数会在 Promise 内部立即同步调用,异步代码需要放在 executor 函数中的函数体位置。

Promise.prototype.then 方法

  • 语法:
const promise = new Promise((resolve,reject) =>{});
promise.then(onFulfilled,onRejected);
  • onFulfilled:成功的回调函数,(value) =>{}
  • onRejected:成功的回调函数,(reason) =>{}

注意:then 方法会返回一个新的 Promise 实例对象。那么就形成了链式编程。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        /* 注意:then 方法会返回一个新的 Promise 实例对象。 */
        const promise = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(100);
            }, 1000);
        });

        let p = promise.then(value => {
            console.log(value);
        }, reason => {
            console.log(reason);
        });

        console.log(p);
        console.log(promise === p); // false
        console.log(p instanceof Promise); // true
    </script>
</body>
</html>

image.png

Promise.prototype.catch 方法

  • 语法:
const promise = new Promise((resolve,reject) =>{});
promise.catch(onRejected);
  • onRejected:失败的回调函数,(reason) =>{}

注意:catch 方法是 then 方法的语法糖,相当于 then(onfulfilled,onRejected)。

示例:

<script>
    /* Promise.prototype.catch 方法 */
  const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(-100);
  }, 1000);
});

promise.then(value => {
  console.log("value", value);
}, reason => {
  console.log("reason", reason);
})

promise.catch(reason => {
  console.log(reason);
});
</script>

image.png

Promise.resolve 方法

  • 语法:
Promise.resolve(value);
  • 用于快速返回一个状态为 fulfilled 或 rejected 的 Promise 实例。

注意:value 的值可能是 非Promise 的值(返回成功)或 Promise 的值(如果是失败的 Promise ,返回失败)。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        /* Promise.resolve 方法 */
        const p = Promise.resolve(100);
        p.then(value => {
            // 100
            console.log(value);
        }, reason => {
        });
    </script>
</body>
</html>

输出 100
image.png

Promise.reject 方法

  • 语法:
Promise.reject(reason);
  • 用于快速返回一个状态必为 rejected 的 Promise 实例。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        /* Promise.reject 方法 */
        const p = Promise.reject(100);
        p.then(value => {
            console.log(value);
        }, reason => {
            // 100
            console.error(reason);
        });
        console.log(p)
    </script>
</body>
</html>

image.png

image.png

Promise.all 方法

  • 语法:
Promise.all(promiseArr)
  • promiseArr:包含 n 个 Promise 实例的数组。
  • 返回一个新的 Promise 实例,只有所有的 Promise 实例成功才成功,只要有一个失败就直接失败。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        /* Promise.all 方法 */
        const p1 = Promise.resolve(1);
        const p2 = Promise.resolve(2);
        const p3 = Promise.resolve(3);

        const p = Promise.all([p1, p2, p3]);
        p.then(value => {
            // [1, 2, 3]
            console.log('value', value);
        }, reason => {
            console.log('reason', reason);
        });
    </script>
</body>
</html>

image.png

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        /* Promise.all 方法 */
        const p1 = Promise.resolve(1);
        const p2 = Promise.reject(-2);
        const p3 = Promise.resolve(3);

        const p = Promise.all([p1, p2, p3]);
        p.then(value => {
            console.log('value', value);
        }, reason => {
            // reason -2
            console.log('reason', reason);
        });
    </script>
</body>
</html>

image.png

Promise.race 方法

  • 语法:
Promise.race(promiseArr)
  • promiseArr:包含 n 个 Promise 实例的数组。
  • 返回一个新的 Promise 实例,以最先出结果的 Promise 实例为准。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        /* Promise.race 方法 */
        const p1 = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(222);
            }, 3000);
        });
        const p2 = new Promise((resolve, reject) => {
            setTimeout(() => {
                reject(222);
            }, 2000);
        });
        const p3 = new Promise((resolve, reject) => {
            setTimeout(() => {
                reject(333);
            }, 1000);
        });

        const p = Promise.race([p1, p2, p3]);
        p.then(value => {
            console.log('value', value);
        }, reason => {
            // reason 333
            console.log('reason', reason);
        });
    </script>
</body>
</html>

image.png

使用注意事项

如何改变一个 Promise 实例的状态?

① 执行 resolve(value) :如果当前是 pending ,就会变为 fulfilled 。
② 执行 reject(reason):如果当前是 pending ,就会变为 fulfilled 。
③ 执行器函数(executor)抛出异常:如果当前是 pending,就会变为 rejected 。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        /*
        * 如何改变一个 Promise 实例的状态?
        * ① 执行 resolve(value) :如果当前是 pending ,就会变为 fulfilled 。
        * ② 执行 reject(reason):如果当前是 pending ,就会变为 fulfilled 。
        * ③ 执行器函数(executor)抛出异常:如果当前是 pending,就会变为 rejected 。
        */
        const p = new Promise((resolve, reject) => {
            // 函数体
            throw new Error('出错啦');
        });

        p.then(value => {
            console.log(`value = ${value}`);
        }, reason => {
            // reason = Error: 出错啦
            console.log(`reason = ${reason}`);
        });
    </script>
</body>
</html>

改变 Promise 实例的状态和指定回调函数谁先谁后?

① 都有可能,正常情况下是先指定回调再改变状态,但是也可以先改变状态再指定回调?
② 如何先改状态再指定回调?只需要延迟一会再调用 then() 方法。
③ Promise 实例什么时候能得到数据?

  • 如果先指定的回调,那么当状态发生改变的时候,回调函数就会调用,得到数据。
  • 如果先改变的状态,那么当指定回调的时候,回调函数就会调用,得到数据。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        /*
        * 改变 Promise 实例的状态和指定回调函数谁先谁后?
        * ① 都有可能,正常情况下是先指定回调再改变状态,但是也可以先改变状态再指定回调。
        * ② 如何先改状态再指定回调?只需要延迟一会再调用 then() 方法。
        * ③ Promise 实例什么时候能得到数据?
        *   如果先指定的回调,那么当状态发生改变的时候,回调函数就会调用,得到数据。
        *   如果先改变的状态,那么当指定回调的时候,回调函数就会调用,得到数据。
        */
        // 正常情况下是先指定回调再改变状态
        const p = new Promise((resolve, reject) => {
            // 函数体
            setTimeout(() => {
                resolve(11);
            }, 1000);
        });
        p.then(value => {
            // 函数体
            console.log('成功了', value);
        }, reason => {
            // 函数体
            console.log('失败了', reason);
        });
    </script>
</body>
</html>
  • 示例:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        /*
        * 改变 Promise 实例的状态和指定回调函数谁先谁后?
        * ① 都有可能,正常情况下是先指定回调再改变状态,但是也可以先改变状态再指定回调。
        * ② 如何先改状态再指定回调?只需要延迟一会再调用 then() 方法。
        * ③ Promise 实例什么时候能得到数据?
        *   如果先指定的回调,那么当状态发生改变的时候,回调函数就会调用,得到数据。
        *   如果先改变的状态,那么当指定回调的时候,回调函数就会调用,得到数据。
        */
        // 先改变状态再指定回调
        const p = new Promise((resolve, reject) => {
            // 函数体
            resolve(11);
        });
        setTimeout(() => {
            p.then(value => {
                // 函数体
                console.log('成功了', value);
            }, reason => {
                // 函数体
                console.log('失败了', reason);
            });
        }, 2000);
    </script>
</body>
</html>

then 的链式调用

  • 『问』Promise实例.then() 返回的是一个新 Promise 实例,它和值和状态由什么决定?
  • 『答』:
    • 简单表达:由 then() 所指定的回调函数执行的结果决定的。
    • 详细表达:
      • 如果 then 所指定的回调返回的是 非Promise 值 a ,那么新 Promise 实例状态为成功(fulfilled),成功的 value 为 a。
      • 如果 then 所指定的回调返回的是一个 Promise 实例 p ,那么新 Promise 实例的状态和值都和 p 一致。
      • 如果 then 所指定的回调抛出异常,那么新Promise实例状态为 rejected ,reason 为抛出的异常。


示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        /*
        *『问』Promise实例.then() 返回的是一个 `新 Promise 实例` ,它和值和状态由什么决定?
        *『答』:
        *   简单表达:由 then() 所指定的回调函数执行的结果决定的。
        *   详细表达:
        *    如果 then 所指定的回调返回的是 `非Promise 值 a` ,那么`新 Promise 实例`状态为成功(fulfilled),成功的 value 为 a。
        *    如果 then 所指定的回调返回的是一个 `Promise 实例 p` ,那么`新 Promise 实例`的状态和值都和 p 一致。
        *    如果 then 所指定的回调`抛出异常`,那么`新Promise实例`状态为 rejected ,reason 为抛出的异常。
        */
        const p = new Promise((resolve, reject) => {
            // 函数体
            setTimeout(() => {
                resolve('a');
            }, 1000);
        });
        const x = p.then(value => {
            // 函数体
            console.log('成功1', value); // 成功1 a
        }, reason => {
            // 函数体
            console.log('失败1', reason);
        });

        x.then(value => {
            // 函数体
            console.log('成功2', value); // 成功2 undefined
        }, reason => {
            // 函数体
            console.log('失败2', reason);
        });

    </script>
</body>
</html>
  • 示例:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        /*
        *『问』Promise实例.then() 返回的是一个 `新 Promise 实例` ,它和值和状态由什么决定?
        *『答』:
        * ① 简单表达:由 then() 所指定的回调函数执行的结果决定的。
        * ② 详细表达:
        *    如果 then 所指定的回调返回的是 `非Promise 值 a` ,那么`新 Promise 实例`状态为成功(fulfilled),成功的 value 为 a。
        *    如果 then 所指定的回调返回的是一个 `Promise 实例 p` ,那么`新 Promise 实例`的状态和值都和 p 一致。
        *    如果 then 所指定的回调`抛出异常`,那么`新Promise实例`状态为 rejected ,reason 为抛出的异常。
        */
        const p = new Promise((resolve, reject) => {
            // 函数体
            setTimeout(() => {
                resolve('a');
            }, 1000);
        });

        // 链式调用
        p.then(value => {
            // 函数体
            console.log('成功1', value); // 成功1 a
        }, reason => {
            // 函数体
            console.log('失败1', reason);
        }).then(value => {
            // 函数体
            console.log('成功2', value); // 成功2 undefined
        }, reason => {
            // 函数体
            console.log('失败2', reason);
        });

    </script>
</body>
</html>

纯回调引起的回调地狱问题

  • 纯回调会引起回调地狱问题。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        // 用户数据 ==> 订单数据 ==> 商品数据
        setTimeout(() => {
            console.log('用户数据');
            
            setTimeout(() => {
                console.log('订单数据');
            
                setTimeout(() => {
                    console.log('商品数据');
                }, 1000);
                
            }, 1000);
            
        }, 1000);
    </script>
</body>
</html>

使用 Promise 实例的 then() 方法解决回调地狱问题

  • Promise 实例的 then() 的链式调用可以解决回调地狱问题。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        // 用户数据 ==> 订单数据 ==> 商品数据
        const p = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('用户数据');
            }, 1000);
        });
        p.then(value => {
            console.log(value);
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('订单数据');
                }, 1000);
            });
        }, reason => {
            console.error(reason);
        }).then(value => {
            console.log(value);
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('商品数据');
                }, 1000);
            });
        }, reason => {
            console.error(reason);
        }).then(value => {
            console.log(value);
        }, reason => {
            console.error(reason);
        });
    </script>
</body>
</html>

中断 Promise 链

  • 『问』当使用 Promise 实例的 then 链式调用时,需要在中间中断,不再调用后面的回调函数?
  • 『答』在回调函数中返回一个 pending 状态的 Promise 对象。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        // 只要有一个请求出现问题,就不再发送请求:用户数据 ==> 订单数据 ==> 商品数据

        // 当使用 Promise 实例的 then 链式调用时,需要在中间中断,不再调用后面的回调函数?
        // 在回调函数中返回一个 pending 状态的 Promise 对象。

        const p = new Promise((resolve, reject) => {
            setTimeout(() => {
                reject('用户数据');
            }, 1000);
        });
        p.then(value => {
            console.log('第一次请求成功', value);
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('订单数据');
                }, 1000);
            });
        }, reason => {
            console.error('第一次请求失败', reason);
            return new Promise(() => {
            });
        }).then(value => {
            console.log('第二次请求成功', value);
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('商品数据');
                }, 1000);
            });
        }, reason => {
            console.error('第二次请求失败', reason);
            return new Promise(() => {
            });
        }).then(value => {
            console.log('第三次请求成功', value);
        }, reason => {
            console.error('第三次请求失败', reason);
        });
    </script>
</body>
</html>

Promise 异常穿透

  • 当使用 Promise 实例的 then 链式调用时,可以在最后使用 catch 捕获一个事变的回调。当前面任何操作出现了问题,都会传递到最后失败的回调中进行处理。
  • 注意:如果不存在 then 的链式调用,则不需要考虑 then 的异常穿透。


示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        // 异常穿透:当使用 Promise 实例的 then 链式调用时,可以在最后使用 catch 捕获一个事变的回调。当前面任何操作出现了问题,都会传递到最后失败的回调中进行处理。
        const p = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('用户数据');
            }, 1000);
        });
        p.then(value => {
            console.log('第一次请求成功', value);
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    reject('订单数据');
                }, 1000);
            });
        }).then(value => {
            console.log('第二次请求成功', value);
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('商品数据');
                }, 1000);
            });
        }).then(value => {
            console.log('第三次请求成功', value);
        }).catch(reason => {
            console.error(reason);
        });
    </script>
</body>
</html>

async 和 await

简介

  • async 和 await 两种语法的结合可以将异步代码像同步代码一样。
  • await 必须包含在 async 修饰的函数中。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        const p = new Promise((resolve, reject) => {
            // 函数体
            setTimeout(() => {
                reject(1);
            }, 1000);
        });

        async function demo() {
            try {
                // await 只能返回 Promise 实例对象成功的值
                const result = await p;
                console.log(result);
            } catch (e) {
                console.log(e);
            }
        }

        demo();

    </script>
</body>
</html>


示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>
        const p1 = new Promise((resolve, reject) => {
            // 函数体
            setTimeout(() => {
                resolve(1);
            }, 1000);
        });

        const p2 = new Promise((resolve, reject) => {
            // 函数体
            setTimeout(() => {
                resolve(2);
            }, 2000);
        });

        const p3 = new Promise((resolve, reject) => {
            // 函数体
            setTimeout(() => {
                resolve(3);
            }, 3000);
        });

        // 可以使用立即执行函数简化,这种方式和 Promise 实例对象的 then 方法执行的结果是一样的。
        ;(async () => {
            try {
                const result1 = await p1;
                console.log(result1);
                const result2 = await p2;
                console.log(result2);
                const result3 = await p3;
                console.log(result3);
            } catch (e) {
                console.error(e);
            }
        })();

    </script>
</body>
</html>

使用 async 和 await 解决回调地狱问题

  • async 和 awit 可以优雅的解决回调地狱问题,比 Promise 的异常穿透更为优雅。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>使用 async 和 await 解决回调地狱</title>
</head>
<body>
    <script>
        // 用户数据 ==> 订单数据 ==> 商品数据

        ;(async () => {
            try {
                let value = await new Promise((resolve, reject) => {
                    setTimeout(() => {
                        resolve('用户数据');
                    }, 1000);
                });
                console.log('第一次请求成功', value);
                value = await new Promise((resolve, reject) => {
                    setTimeout(() => {
                        resolve('订单数据');
                    }, 1000);
                });
                console.log('第二次请求成功', value);
                value = await new Promise((resolve, reject) => {
                    setTimeout(() => {
                        reject('商品数据');
                    }, 1000);
                });
                console.log('第三次请求成功', value);
            } catch (e) {
                console.error('请求失败', e);
            }
        })();

    </script>
</body>
</html>

image.png
image.png

async 和 await 的规则

  • async 修饰的函数:
    • 函数的返回值是 Promise 对象。
    • Promise 实例的结果由 async 函数执行的返回值决定。
  • await 表达式:
    • await 右侧的表达式一般为 Promise 实例对象,但是也可以是其他的值。
    • 如果表达式是 Promise 对象,await 的返回值是 Promise 成功的值。
    • 如果表达式是其他值,直接将此值作为 await 的返回值。

注意:

  • await 必须写在 async 函数中,但是 async 函数中可以没有 await (ES7 规定的)。
  • 如果 await 的 Promise 实例失败了,就会抛出异常,需要通过 try…catch 进行捕获。

await 的原理

  • 如果我们使用 async 配合 await :表面上不会出现任何回调函数,但是实际上底层是将我们的代码进行的加工,将回调函数还原了回来,最终运行的代码依然有回调,只是程序员看不见而已。
  • 换言之,async 配合 await 就是 Promise 中 then 的语法糖。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>await 原理</title>
</head>
<body>
    <script>

        const p = new Promise((resolve, reject) => {
            // 函数体
            setTimeout(() => {
                resolve(1);
            }, 2000);
        });

        async function demo() {
            // 相当于 p.then(value=>{  console.log(result) })
            const result = await p; 
            console.log(result);
        }

        demo();

    </script>
</body>
</html>


image.png

宏队列和微队列

  • JS 中用来存储待执行回调函数的队列包含 2 个不同特定的列队。
  • 宏列队:用来保存待执行的宏任务(回调), 比如: 定时器回调/DOM 事件回调/ajax 回调。
  • 微列队:用来保存待执行的微任务(回调), 比如: promise 的回调/MutationObserver 的回调。
  • JS 执行时会区别这 2 个队列:
    • JS 引擎首先必须先执行所有的初始化同步任务代码。
    • 每次准备取出第一个宏任务执行前,都要将所有的微任务一个一个取出来执行。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="IE=edge" http-equiv="X-UA-Compatible">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Title</title>
</head>
<body>
    <script>

        setTimeout(() => {
            console.log('timeout');
        }, 0);

        Promise.resolve(1).then(value => {
            console.log('成功1', value);
        });

        Promise.resolve(2).then(value => {
            console.log('成功2', value);
        });

        console.log('主线程');

    </script>
</body>
</html>

image.png

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值