【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); // falseconsole.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 => {// 100console.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 => {// 100console.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 -2console.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 333console.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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/624616.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenHarmony实战开发-如何实现发布图片评论功能。

介绍 本示例将通过发布图片评论场景&#xff0c;介绍如何使用startAbilityForResult接口拉起相机拍照&#xff0c;并获取相机返回的数据。 效果图预览 使用说明 通过startAbilityForResult接口拉起相机&#xff0c;拍照后获取图片地址。 实现思路 1.创建CommentData类&…

【LatentDiffusion 代码详解(1)】LatentDiffusion 的 yaml 解读

YAML 文件提供了一种清晰、简洁且易于理解的方式来描述配置信息&#xff0c;特别适用于机器学习模型的超参数调优和实验管理。 以 Latent Diffusion 官方代码仓库中的 https://github.com/CompVis/latent-diffusion/blob/main/configs/autoencoder/autoencoder_kl_32x32x4.yam…

了解 Unity AI:从初学者到高级的综合指南

游戏中的AI是什么? 游戏中的人工智能是指利用人工智能技术使视频游戏中的非玩家角色和实体智能地行动、做出决策、对游戏环境做出反应,并提供引人入胜的动态游戏体验。什么是NPC? NPC 代表“非玩家角色”。NPC 是视频游戏、角色扮演游戏中不受人类玩家控制的角色。它们是计算…

Swoole 实践篇之结合 WebRTC 实现音视频实时通信方案

原文首发链接&#xff1a;Swoole 实践篇之结合 WebRTC 实现音视频实时通信方案 大家好&#xff0c;我是码农先森。 引言 这次实现音视频实时通信的方案是基于 WebRTC 技术的&#xff0c;它是一种点对点的通信技术&#xff0c;通过浏览器之间建立对等连接&#xff0c;实现音频…

java线程(1)

1、多线程启动 有两种启动方式 1、实现Runable接口 2、继承Thread类并且重写run&#xff08;&#xff09;方法 在执行进程中的任务才会产生线程&#xff0c;所以需要实现Runable接口并且重写run&#xff08;&#xff09;方法&#xff0c;然后将Runable的实现对象作为参数传…

官宣:2024第二十届国际铸造件展12月精彩呈现!

Shanghai International Die-casting Casting Expo 2024第二十届上海国际压铸、铸造展览会 2024第二十届上海国际压铸、铸件产品展 时间&#xff1a;2024年12月18-20日 地点&#xff1a;上海新国际博览中心&#xff08;浦东区龙阳路2345号&#xff09; 报名参展&#xff1…

Delphi Xe 10.3 钉钉SDK开发——审批流接口(获取表单ProcessCode)

开发钉钉审批流时&#xff0c;需要用到钉钉表单的Processcode&#xff0c;有两种方法 &#xff1a; 一、手动获取&#xff1a; 管理员后台——审批——找到对应的表单&#xff1a;如图&#xff1a; ProcessCode后面就是了&#xff01; 二、接口获取&#xff1a;今天的重点&a…

conda新建环境报错An HTTP error occurred when trying to retrieve this URL.

conda新建环境报错如下 cat .condarc #将 .condarc文件中的内容删除&#xff0c;改成下面的内容 vi .condarc channels:- defaults show_channel_urls: true default_channels:- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main- https://mirrors.tuna.tsinghua.…

《大话数据结构》02 算法

算法是解决特定问题求解步骤的描述&#xff0c;在计算机中表现为指令的有限序列&#xff0c;并且每条指令表示一个或多个操作。 1. 两种算法的比较 大家都已经学过一门计算机语言&#xff0c;不管学的是哪一种&#xff0c;学得好不好&#xff0c;好歹是可以写点小程序了。现在…

Redis安装和使用(Ubuntu系统)

本节内容包括Redis简介、安装Redis和Redis实例演示等&#xff0c;Redis在Window系统安装教程可参考Redis安装与运行_厦大数据库实验室博客 Redis是一个键值&#xff08;key-value&#xff09;存储系统&#xff0c;即键值对非关系型数据库。Redis提供了Python、Ruby、Erlang、P…

【C语言】每日一题,快速提升(3)!

&#x1f525;博客主页&#x1f525;&#xff1a;【 坊钰_CSDN博客 】 欢迎各位点赞&#x1f44d;评论✍收藏⭐ 题目&#xff1a;杨辉三角 在屏幕上打印杨辉三角。 1 1 1 1 2 1 1 3 3 1 ……......... 解答&#xff1a; 按照题设的场景&#xff0c;能发现数字规律为&#xff1…

聚道云软件连接器助力医疗器械有限公司打通金蝶云星辰与飞书

摘要 聚道云软件连接器成功将金蝶云星辰与飞书实现无缝对接&#xff0c;为某医疗器械有限公司解决采购订单、付款单同步、审批结果回传、报错推送等难题&#xff0c;实现数字化转型升级。 客户介绍 某医疗器械有限公司是一家集研发、生产、销售为一体的综合性医疗器械企业。…