Promise使用详解

简介: JS查漏补缺系列是我在学习JS高级语法时做的笔记,通过实践费曼学习法进一步加深自己对其的理解,也希望别人能通过我的笔记能学习到相关的知识点。这一次我们来了解Promise使用详解

异步请求的处理方式

案例要求:模拟网络请求,通过请求结果来调用成功的回调函数/调用失败的回调函数

在没有使用promise之前,需要callback来调用(自己封装好并定义好名称,要使用的时候才能调用)

function requestData(url, successCallback, failureCallback) {
  // 模拟网络请求
  setTimeout(() => {
    // 拿到请求的结果
    // url传入的是aaa, 请求成功
    if (url === "aaa") {
      // 成功
      let names = ["abc", "cba", "nba"]
      successCallback(names)
    } else { // 否则请求失败
      // 失败
      let error = "请求失败, url错误"
      failureCallback(error)
    }
  }, 3000);
}

// 成功的回调函数
function successCallback(result) {
  console.log("请求成功" + result);
}

// 失败的回调函数
function failureCallback(error) {
  console.log(error);
}

requestData("aac", successCallback, failureCallback)

弊端:因为是自定义的回调函数的名称和方法,所以在开发使用的时候要查看源码知道名称和方法后才能调用函数,增加了工作量
更好的方案:统一名称 promise,并规范好所有的代码编写逻辑

Promise

Promise 是一个对象,它代表了一个异步操作的最终完成或者失败。

简单使用:

function foo() {
// 传入的这个函数, 被称之为 executor
// > resolve: 回调函数, 在成功时, 回调resolve函数
// >reject: 回调函数, 在失败时, 回调reject函数
  return new Promise((resolve, reject) => {
    resolve("success message")
    // reject("failture message")
  })
}

const fooPromise = foo()
// 调用resolve(请求成功)会来到then方法
fooPromise.then((res) => {
  console.log(res)
}
// 调用reject(请求失败)会来到catch方法
fooPromise.catch((err) => {
  console.log(err)
})

用promise重构原来的代码去解决案例(异步请求处理):

function requestData(url,) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url === "aaa") {
        // 成功
        let names = ["abc", "cba", "nba"]
        resolve(names)
      } else { 
        // 失败
        let errMessage = "请求失败, url错误"
        reject(errMessage)
      }
    }, 3000);
  })
}

// main.js
const promise = requestData("aaa")
promise.then((res) => {
  console.log("请求成功:", res)
})
promise.catch((err) => {
   console.log("请求失败:", err)
})

在node中then和catch分开会报错
promise中的then和catch可以合并,给then传入两个回调函数:

第一个回调函数, 会在Promise执行resolve函数时, 被回调
第二个回调函数, 会在Promise执行reject函数时, 被回调
const promise = requestData("aaa")
promise.then((res) => {
  console.log("请求成功:", res)
}, (err) => {
   console.log("请求失败:", err)
})

promise的三种状态

一个 Promise 必然处于以下几种状态之一:

  • _待定(pending)_:初始状态,既没有被兑现,也没有被拒绝。(resolve函数和reject函数都没有被调用)
  • _已兑现(fulfilled)_:意味着操作成功完成。(已调用resolve函数)
  • _已拒绝(rejected)_:意味着操作失败。(已调用reject函数)

当resolve函数已经被调用时,promise的状态就被确定了,这时再去调用reject函数是没有效果的,反之亦然。

Promise 的链式调用

连续执行两个或者多个异步操作(一个promise包含着多个promise)
在上一个操作执行成功之后,开始下一个的操作,并带着上一步操作所返回的结果。我们可以通过创造一个 Promise 链来实现连续执行两个或者多个异步操作的需求。

一个promise:

new Promise((resolve, reject) => {
  resolve('res message')
}).then(res => {
  console.log("res:", res)
}, err => {
  console.log("err:", err)
})

两个promise(当前promise的状态由传入promise决定):

const promise = new Promise((resolve, reject) => {
  // resolve("aaaaaa")
  reject("err message")
})

const newPromise = new Promise((resolve, reject) => {
  resolve(Promise)
}).then(res => {
  console.log("res:", res)
}, err => {
  console.log("err:", err)
})

Promise对象方法

// 可以通过下面代码查看Promise有哪些对象方法
console.log(Object.getOwnPropertyDescriptors(Promise.prototype))

Promise.prototype.then()

简单使用:

const promise = new Promise((resolve, reject) => {
  resolve()
})
promise.then(res => {
  console.log('res:',res)
})  // 当上面调用resolve时会调用下面的回调

同一个Promise可以被多次调用then方法,当我们的resolve方法被回调时, 所有的then方法传入的回调函数都会被调用

promise.then(res => {
  console.log("res1:", res)
})

promise.then(res => {
  console.log("res2:", res)
})

promise.then(res => {
  console.log("res3:", res)
})

then方法传入的 "回调函数: 可以有返回值,返回值是新的Promise

  • 如果我们返回的是一个普通值(数值/字符串/普通对象/undefined), 那么这个普通的值被作为一个的Promise的resolve
// 下面是一个链式调用
promise.then(res => {
  return "aaaaaa"  // 没有返回值默认返回undefined
}).then(res => {
  console.log("res:", res)
  return "bbbbbb"
})

上面代码中第二个then方法接收的值是第一个then方法返回的,且上面调用resolve是与第二个then方法无关的(注意:这里两个then方法所捕获的Promise不是同一个,第二个then方法捕获的是第一个then方法返回产生的一个的新Promise)

  • 如果我们返回的是一个Promise,则后一个的then方法所捕获的Promise取决于前一个返回的Promise
promise.then(res => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(111111)
    }, 3000)
  })
}).then(res => {
  console.log("res:", res)
})

Promise.prototype.catch()

通过catch方法来传入错误(拒绝)捕获的回调函数

简单使用:

const promise = new Promise((resolve, reject) => {
  reject()
})
promise.catch(err => {
  console.log('err:',err)
})

注意:catch捕获顺序

  1. 当上面调用的是resolve时:
  • 如果我们的catch方法是写在then方法之后的,当then方法返回的是一个普通值,那then方法和catch方法捕获的是同一个Promise。
  • 但如果then方法返回的是一个新的Promise且调用了reject/throw Error,则这个then方法之后的catch方法优先捕获的是新的Promise
const promise = new Promise((resolve, reject) => {
  resolve('111')
})
promise.then(res => {
  return new Promise((resolve, reject) => {
    reject("then rejected status")
  })
}).catch(err => {
  console.log("err:", err) // err: then rejected status
})
  1. 当上面调用的是reject时:
  • catch方法捕获的是与then方法同一个Promise,无论then方法返回的是什么值
const promise = new Promise((resolve, reject) => {
  reject('111')
})
promise.then(res => {
  return new Promise((resolve, reject) => {
    reject("then rejected status")
  })
}).catch(err => {
  console.log("err:", err) // err: 111
})

catch返回值:返回值也是新的Promise

  • 如果我们返回的是一个普通值(数值/字符串/普通对象/undefined), 那么这个普通的值被作为一个的Promise的resolve值(与then方法一样)
  • 如果我们希望后续继续执行catch,那么需要抛出一个异常

Catch 的后续链式操作

在回调的时候抛出错误之后想要再次进行新的操作则可以使用catch来实现
new Promise((resolve, reject) => {
    console.log('开始回调-------');
    resolve();
})
.then(() => {
    throw new Error('error message');

    console.log('aaa');
})
.catch(() => {
    console.log('bbb');
})
.then(() => {
    console.log('ccc');
});

在还没有开始执行之前,VSCode就已经检测出第一个then里面抛出错误之后的代码不会执行:
image.png
执行输出:
image.png
总结:当出现失败的情况时,使用catch()中断(在catch回调和抛出错误的then回调之间的then回调也是不会执行的,catch回调之后的then回调可以执行)

Promise.prototype.finally()

finally() 方法返回一个 Promise。在 promise 结束时, 无论结果是 fulfilled 或者是 rejected, 会执行指定的回调函数。
这为在 Promise 是否成功完成后都需要执行的代码提供了一种方式。这避免了同样的语句需要在 then()catch() 中各写一次的情况
const promise = new Promise((resolve, reject) => {
  // resolve("resolve message")
  reject("reject message")
})

promise.then(res => {
  console.log("res:", res)
}).catch(err => {
  console.log("err:", err)
}).finally(() => {
  console.log("finally code execute")
})

Promise类方法

直接通过类名调用的方法

Promise.resolve

将普通对象转成Promise对象并调用resolve
  1. 传入普通的值
const promise = Promise.resolve({ name: "why" })
// 相当于
const promise2 = new Promise((resolve, reject) => {
  resolve({ name: "why" })
})
  1. 传入Promise
const promise = Promise.resolve(new Promise((resolve, reject) => {
  resolve("11111")
}))

Promise.reject

将普通对象转成Promise对象并调用reject
const promise = Promise.reject("rejected message")
// 相当于
const promise2 = new Promsie((resolve, reject) => {
  reject("rejected message")
})

Promise.all

所有的Promise都变成fulfilled状态时(所有Promise都调用resolve之后), 再拿到结果
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(11111)
  }, 1000);
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(22222)
  }, 2000);
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(33333)
  }, 3000);
})

Promise.all([p2, p1, p3, "aaaa"]).then(res => {
  console.log(res) // [22222,11111,33333,'aaaa']
})

但是在拿到所有结果之前, 有一个promise变成了rejected状态, 那么整个promise是rejected状态(Promise.all方法会被中断)

Promise.allSettled

所有的Promise都有结果(无论是fulfilled状态还是rejected状态)之后, 再拿到结果
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(11111)
  }, 1000);
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(22222)
  }, 2000);
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(33333)
  }, 3000);
})

// allSettled
Promise.allSettled([p1, p2, p3]).then(res => {
  console.log(res)
}).catch(err => {
  console.log(err)
})

image.png

Promise.race

只要有一个Promise变成fulfilled状态, 那么就结束,但有一个Promise状态先变为rejected,则结束

Promise.any

等到至少有一个Promise变成fulfilled状态, 才就结束,不管有没有Promise状态先变为rejected。如果全为rejected状态则等到全部执行完才结束并执行catch方法(全部rejected状态的错误信息)
目录
相关文章
|
8月前
|
人工智能 算法 API
构建基于 Elasticsearch 的企业级 AI 搜索应用
本文介绍了基于Elasticsearch构建企业级AI搜索应用的方案,重点讲解了RAG(检索增强生成)架构的实现。通过阿里云上的Elasticsearch AI搜索平台,简化了知识库文档抽取、文本切片等复杂流程,并结合稠密和稀疏向量的混合搜索技术,提升了召回和排序的准确性。此外,还探讨了Elastic的向量数据库优化措施及推理API的应用,展示了如何在云端高效实现精准的搜索与推理服务。未来将拓展至多模态数据和知识图谱,进一步提升RAG效果。
305 1
|
存储 算法 安全
|
存储 关系型数据库 MySQL
MySQL中的字符集与排序规则详解
在 MySQL 中,字符集(Character Set)用于确定可以在数据库中存储的字符集合,而排序规则(Collation)用于指定比较和排序字符串的规则。下面是关于 MySQL 中字符集和排序规则的一些详细信息: 1. 字符集(Character Set): - 字符集定义了可用于存储数据的字符集合。 - MySQL 支持多种字符集,包括常见的 UTF-8、GBK、Latin1 等。 - 你可以在创建数据库、表或列时指定所需的字符集。 2. 排序规则(Collation): - 排序规则定义了比较字符串时使用的规则。 - 比较和排序规则根据不同的语言和地区
795 0
|
7天前
|
人工智能 运维 安全
|
5天前
|
人工智能 异构计算
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
|
7天前
|
机器学习/深度学习 人工智能 自然语言处理
B站开源IndexTTS2,用极致表现力颠覆听觉体验
在语音合成技术不断演进的背景下,早期版本的IndexTTS虽然在多场景应用中展现出良好的表现,但在情感表达的细腻度与时长控制的精准性方面仍存在提升空间。为了解决这些问题,并进一步推动零样本语音合成在实际场景中的落地能力,B站语音团队对模型架构与训练策略进行了深度优化,推出了全新一代语音合成模型——IndexTTS2 。
626 22
|
6天前
|
人工智能 测试技术 API
智能体(AI Agent)搭建全攻略:从概念到实践的终极指南
在人工智能浪潮中,智能体(AI Agent)正成为变革性技术。它们具备自主决策、环境感知、任务执行等能力,广泛应用于日常任务与商业流程。本文详解智能体概念、架构及七步搭建指南,助你打造专属智能体,迎接智能自动化新时代。
|
12天前
|
人工智能 JavaScript 测试技术
Qwen3-Coder入门教程|10分钟搞定安装配置
Qwen3-Coder 挑战赛简介:无论你是编程小白还是办公达人,都能通过本教程快速上手 Qwen-Code CLI,利用 AI 轻松实现代码编写、文档处理等任务。内容涵盖 API 配置、CLI 安装及多种实用案例,助你提升效率,体验智能编码的乐趣。
1003 110