我有一个第三方程序包下面的功能,我不能修改

async function runTransaction(callback) {
   const client = await createClient();
   try {
       await client.query("BEGIN");
       await callback(client);
   } finally {
       await client.query("COMMIT");
   }
}

通常,我必须创建一个pixelc函数,并将其传递给runTransaction

async function update(client) {
   await client.query("UPDATE ...")
}


await runTransaction(update);

我想知道是否有一种方法可以在异步生成器模式中使用runTransaction.这是我的素描.我想要的最终结果是BEGIN,然后UPDATE,然后COMMIT


async *clientGenerator() {
   await runTransaction(async client => {
      yield client
   });
   // this will not work as yield can't be used in a callback
}


async function main() {
   const dbClientGenerator = await clientGenerator();
   const dbClientHolder = await dbClientGenerator.next();
   await dbClientHolder.value.query("UPDATE ...");
}

我在下面也try 了,但回调与runTransaction中的await client.query("COMMIT");同时被调用.

async *clientGenerator() {
   yield new Promise(resolve => {
      runTransaction(async client => {
          resolve(sql);
      })
    })
}

你知道怎么做到吗?

推荐答案

您可以写为1

async function* transaction() {
    const client = await createClient();
    try {
        await client.query("BEGIN");
        yield client;
    } finally {
        await client.query("COMMIT");
    }
}

并将其用作

for await (const client of transaction()) {
    await client.query("UPDATE ...");
}

但这比目前的水平要高出no better (and much more confusing)

await runTransaction(async client => {
    await client.query("UPDATE ...");
});

这真的没有什么错.它被称为promise disposer pattern,并确保在您完成后对资源进行适当的清理.手动推进异步迭代器的main函数是这种方法错误之处的主要示例:它无法进行错误处理,并且永远不会提交事务.promise 处理者模式防止了这种滥用.

这种模式可能会被explicit resource management proposal美元的一次性用品所取代,这将允许书写

{
    await using client = transaction();
    await client.query("UPDATE ...");
}

1:或者甚至把你得到的runTransaction个包装成一个等同的东西,而不是重写它,但这很复杂,我想把这一点弄清楚.


如果你真的必须这么做,以下是方法:

async function* transaction() {
    // © Bergi, https://stackoverflow.com/a/78043294/1048572, CC BY-SA 4.0
    let final, result, resume; 
    const client = await new Promise((resolve, reject) => {
        final = runTransaction(client => {
            resolve(client);
            return new Promise(resolve => {
                resume = resolve;
            });
        })
        final.catch(reject);
    })
    try {
        result = yield client;
    } catch(err) {
        result = Promise.reject(err);
    } finally {
        resume(result);
        return final;
    }
}

这将处理runTransaction中和回调迭代中所有可能的失败情况,但依赖于runTransaction恰好调用回调一次(除非createClient中有错误).

Javascript相关问答推荐

如何将拖放功能添加到我已自定义为图像的文件输入HTML标签中?

使用JavaScript单击上一个或下一个特定按钮创建卡滑动器以滑动单个卡

基于变量切换隐藏文本

无法在nextjs应用程序中通过id从mongoDB删除'

Exceljs:我们在file.xlsx(...)&#中发现了一个问题'"" 39人;

从WooCommerce Checkout Country字段重新排序国家,保持国家同步

在这种情况下,如何 for each 元素添加id?

数字时钟在JavaScript中不动态更新

CheckBox作为Vue3中的一个组件

setcallback是什么时候放到macrotask队列上的?

Javascript json定制

未定义引用错误:未定义&Quot;而不是&Quot;ReferenceError:在初始化&Quot;之前无法访问';a';

如何创建返回不带`new`关键字的实例的类

使用带有HostBinding的Angular 信号来更新样式?

VSCode中出现随机行

第一项杀死下一项,直到数组长度在javascript中等于1

使用父标签中的Find函数查找元素

如何访问此数组中的值?

TypeORM QueryBuilder限制联接到一条记录

Chrome上的印度时区名S有问题吗?