0

0

Prisma Client Extensions 中异步计算字段的解决方案

聖光之護

聖光之護

发布时间:2025-09-26 21:28:01

|

322人浏览过

|

来源于php中文网

原创

prisma client extensions 中异步计算字段的解决方案

Prisma Client Extensions 的 result 扩展旨在提供同步计算字段,直接在 compute 函数中使用 await 会导致 Promise 对象被返回或挂起。本文将深入探讨这一限制,并提供两种有效的解决方案:一是让 compute 函数返回一个异步函数,将异步操作延迟到实际调用时执行;二是利用 model 扩展定义自定义的异步查询方法,以实现更灵活的数据处理和聚合。

理解 Prisma result 扩展的同步特性

Prisma Client Extensions 引入的 result 扩展允许开发者为模型添加派生字段。然而,其 compute 函数被设计为同步执行,以便在访问时以最小开销计算字段。这意味着,如果在 compute 函数内部直接调用异步函数并尝试使用 await,例如从外部服务获取数据,结果将不会是期望的最终值,而是处于 pending 状态的 Promise 对象,或者 Promise 对象本身被字符串化。

示例问题代码:

async function getdata(): Promise {
  return "external data here!";
}

import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient().$extends({
  result: {
    user: {
      nameAndAge: {
        needs: { name: true, age: true },
        // 尝试在 compute 中直接使用 await
        async compute(user) {
          return `${user.name} (${user.age}y) ${await getdata()}`;
        },
      },
    },
  },
});

async function main() {
  const user = await prisma.user.findFirst();
  console.log(user?.nameAndAge); // 输出: Promise {  }
}

main();

这种行为是 Prisma 设计使然,旨在保持 result 扩展的高性能和低开销。对于需要异步操作的场景,Prisma 提供了其他更合适的扩展点。

解决方案一:通过返回异步函数延迟计算

一种解决 compute 函数同步限制的方法是,让 compute 函数本身返回一个异步函数。这样,异步操作的执行就被延迟到实际需要该计算字段值的时候。

实现方式:

将 compute 函数设计为返回一个 async 函数。当访问这个扩展字段时,你需要显式地 await 这个返回的异步函数来获取最终结果。

示例代码:

async function getdata(): Promise {
  return "external data here!";
}

import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient().$extends({
  result: {
    user: {
      // 字段名可以更具描述性,例如 getNameAndAge
      getNameAndAge: {
        needs: { name: true, age: true },
        compute(user) {
          // compute 函数返回一个异步函数
          return async () => (`${user.name} (${user.age}y) ${await getdata()}`);
        },
      },
    },
  },
});

async function main() {
  const users = await prisma.user.findMany(); // 获取用户列表

  if (users.length > 0) {
    // 访问扩展字段时,需要 await 调用返回的异步函数
    const joeNameAndAge = await users[0].getNameAndAge();
    console.log(joeNameAndAge); // 输出: Sonia Lomo (25y) external data here!
  }
}

main();

注意事项:

  • 这种方法将异步计算的责任推给了调用方,调用方必须显式地 await 返回的异步函数。
  • 字段名应清晰表明它是一个需要被调用的函数(例如 getNameAndAge() 而不是 nameAndAge)。
  • 计算是在访问时按需执行,这对于某些场景可能更高效,避免不必要的异步调用。

解决方案二:利用 model 扩展定义自定义异步查询

对于更复杂的数据聚合、需要提前执行异步操作或希望在查询层面控制数据处理的场景,model 扩展提供了更强大的能力。通过 model 扩展,你可以在现有模型上定义全新的异步方法,这些方法可以完全控制查询逻辑和数据转换。

QIMI奇觅
QIMI奇觅

美图推出的游戏行业广告AI制作与投放一体化平台

下载

实现方式:

在 model 扩展中定义一个异步方法,该方法可以执行 Prisma 客户端的查询,并在返回结果之前进行异步的数据处理和字段添加。

示例代码:

async function getdata(): Promise {
  return "external data here!";
}

import { PrismaClient, Prisma } from "@prisma/client";

// 定义自定义查询方法的参数类型
export type UserFindManyWithData = {
  where?: Prisma.UserWhereInput;
  select?: Prisma.UserSelect;
};

const prisma = new PrismaClient().$extends({
  model: {
    user: {
      async findManyWithData({ where, select }: UserFindManyWithData) {
        // 执行原始查询
        const users = await prisma.user.findMany({ where, select });

        // 遍历结果,进行异步数据处理并添加新字段
        // 注意:这种逐个 await 的方式在处理大量数据时可能影响性能
        // 更好的做法是使用 Promise.all() 并行处理异步操作
        for (const user of users) {
          // 假设 user 类型可以动态添加属性
          // 在实际应用中,你可能需要一个更具体的类型定义来包含 nameAndAge
          (user as any).nameAndAge = `${user.name} (${user.age}y) ${await getdata()}`;
        }

        return users;
      },
    },
  },
});

async function main() {
  const usersWithData = await prisma.user.findManyWithData({
    where: { age: { gte: 18 } },
    select: { id: true, name: true, age: true },
  });

  console.log(usersWithData);
  // 输出:
  // [
  //   { id: '...', name: 'Sonia Lomo', age: 25, nameAndAge: 'Sonia Lomo (25y) external data here!' },
  //   ...
  // ]
}

main();

性能考量:

在上述 model 扩展的示例中,for...of 循环内部的 await 会导致异步操作串行执行。如果 getdata() 是一个耗时操作,并且需要处理大量用户数据,这可能会严重影响性能。为了优化性能,建议使用 Promise.all() 并行处理所有用户的异步数据获取:

// ... (其他代码相同)

const prisma = new PrismaClient().$extends({
  model: {
    user: {
      async findManyWithData({ where, select }: UserFindManyWithData) {
        const users = await prisma.user.findMany({ where, select });

        // 使用 Promise.all 并行处理所有用户的异步数据
        const processedUsers = await Promise.all(
          users.map(async (user) => {
            const externalData = await getdata();
            return {
              ...user,
              nameAndAge: `${user.name} (${user.age}y) ${externalData}`,
            };
          })
        );

        return processedUsers;
      },
    },
  },
});

// ... (main 函数相同)

何时选择 model 扩展:

  • 需要在一个查询操作中聚合来自多个源的数据。
  • 希望在数据返回给应用层之前完成所有异步处理。
  • 自定义查询逻辑,例如根据特定条件动态添加字段。
  • 处理大量数据时,可以更好地控制性能优化(例如使用 Promise.all)。

总结

Prisma Client Extensions 的 result 扩展适用于同步计算的派生字段。当需要集成异步操作时,开发者应避免在 compute 函数中直接使用 await。本文提供了两种有效的替代方案:

  1. 返回异步函数: 适用于异步计算相对简单,且希望将计算延迟到实际访问字段时执行的场景。它要求调用方显式地 await 扩展字段。
  2. 使用 model 扩展: 适用于需要更复杂的异步数据聚合、在查询层面进行数据转换,或需要精细控制异步操作性能的场景。它允许定义全新的、完全异步的查询方法。

根据你的具体需求和性能考量,选择最适合的 Prisma Client Extensions 方案,可以有效地扩展 Prisma 客户端的功能,同时保持代码的清晰性和效率。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

298

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

212

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1501

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

624

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

633

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

588

2024.04.29

go语言字符串相关教程
go语言字符串相关教程

本专题整合了go语言字符串相关教程,阅读专题下面的文章了解更多详细内容。

171

2025.07.29

c++字符串相关教程
c++字符串相关教程

本专题整合了c++字符串相关教程,阅读专题下面的文章了解更多详细内容。

83

2025.08.07

clawdbot ai使用教程 保姆级clawdbot部署安装手册
clawdbot ai使用教程 保姆级clawdbot部署安装手册

Clawdbot是一个“有灵魂”的AI助手,可以帮用户清空收件箱、发送电子邮件、管理日历、办理航班值机等等,并且可以接入用户常用的任何聊天APP,所有的操作均可通过WhatsApp、Telegram等平台完成,用户只需通过对话,就能操控设备自动执行各类任务。

11

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 8万人学习

好课诞生记
好课诞生记

共20课时 | 6.1万人学习

swift开发文档
swift开发文档

共33课时 | 21.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号