如何使用执行者将承诺注入承诺链?

How to inject a promise into a promise chain with an executor?

我想要完成的事情

我目前正在尝试为数据库连接(到 Neo4j)创建一个包装器,其工作方式类似于以下内容:

  1. 实例化驱动程序
  2. 公开驱动程序的执行程序以便创建会话
  3. 通过我的逻辑
  4. 关闭连接

由于 JavasScript 中没有析构函数,因此正确关闭会话会造成不必要的困难。创建和关闭连接的逻辑非常重复,我正在努力简化重复的脚本,以便更容易调用。


我试过的。

在链中注入承诺

我认为像下面这样的东西可以工作,但我似乎无法正确地创建逻辑。将 session 传递回我插入的承诺是具有挑战性的。

const connect = () => {
    var driver;
    var session;

    return Promise.resolve(() => {
        driver = my.driver(uri, creds);
    }).then(() => {
        // insert my promise here, exposing driver.session() function for executor
        // if possible, bind it to the session var so we can properly close it after
        // this would also determine the return value
    }).catch((e) => console.error(e))
    .finally(() => {
        session.close();
        driver.close();
    })
});

创建class 具有过程逻辑的包装器

然后我也尝试了另一种方法,类似于:

var driver = my.driver(uri, creds);
var session;

function exitHandler(options) {
    // ...
    session.close();
    driver.close();
}

// I have process.on for exit, SIGINT, SIGUSR1, SIGUSR2, and uncaughtException
process.on('exit', exitHandler.bind(null, options));
// ...

class Connection extends Promise<any> {
    constructor(executor: Function) {
        super((resolve, reject) => executor(resolve, reject));
        executor(driver.session.bind(null, this));
    }
}

export default Connection;

并且这样称呼它

// ...

const handler = async () => await new Connection((session) => {
    const s = session();
    // do stuff here
});

此方法的问题是在使用 session 之前未实例化驱动程序(因此未定义)。 process.on 调用也让人觉得有点老套。


问题

两种方法都不起作用(或我的任何其他尝试)。我如何正确包装数据库连接以确保它们一致并删除现有代码的重复数据?

可以找到 Neo4j 连接脚本的示例 here。从本质上讲,这就是我试图在我的脚本中删除重复数据的内容(将所有内容从第 11 行传递到第 42 行 - 包括在内)但是有驱动程序的初始化,catch,最后,session.close(), driver.close () 包装器中的逻辑。

理想情况下,我想公开会话函数调用,以便我可以在需要时将参数传递给它:有关详细信息,请参阅 Session API。如果可能的话,我还想绑定 rxSession 反应会话。

A sample of the Neo4j connection script can be found here. This is, essentially, what I'm trying to deduplicate across my scripts (pass everything from line 11 to 42 - inclusive) but have the init of driver, catch, finally, session.close(), driver.close() logic in my wrapper.

好的,您所问的上述部分是我能够最好地解析和处理的内容。

获取您引用的代码并分解出第 11 到 42 行,以便共享这些代码之外的所有内容,而其中的所有内容都可以由调用者自定义,这就是我得到的可重用部分,旨在用于一个模块本身:

// dbwrapper.js

const neo4j = require('neo4j-driver')

const uri = 'neo4j+s://<Bolt url for Neo4j Aura database>';
const user = '<Username for Neo4j Aura database>';
const password = '<Password for Neo4j Aura database>';

const driver = neo4j.driver(uri, neo4j.auth.basic(user, password));
let driverOpen = true;

async function runDBOperation(opCallback, sessOpts = {}) {
    const session = driver.session(sessOpts);
    try {
        await opCallback(session);
    } catch (e) {
        console.log(e);
        throw e;
    } finally {
        await session.close();
    }
}

async function shutdownDb() {
    if (driverOpen) {
        driverOpen = false;
        await driver.close();
    }
}

process.on('exit', shutdownDb);

module.exports = { runDBOperation, shutdownDb };

然后,您可以像这样从其他模块使用它:

const { runDBOperation, shutdownDB } = require('./dbwrapper.js');

runDBOperation(async (session) => {
    const person1Name = 'Alice'
    const person2Name = 'David'

    // To learn more about the Cypher syntax, see https://neo4j.com/docs/cypher-manual/current/
    // The Reference Card is also a good resource for keywords https://neo4j.com/docs/cypher-refcard/current/
    const writeQuery = `MERGE (p1:Person { name: $person1Name })
                      MERGE (p2:Person { name: $person2Name })
                      MERGE (p1)-[:KNOWS]->(p2)
                      RETURN p1, p2`

    // Write transactions allow the driver to handle retries and transient errors
    const writeResult = await session.writeTransaction(tx =>
        tx.run(writeQuery, { person1Name, person2Name })
    )
    writeResult.records.forEach(record => {
        const person1Node = record.get('p1')
        const person2Node = record.get('p2')
        console.log(
            `Created friendship between: ${person1Node.properties.name}, ${person2Node.properties.name}`
        )
    })

    const readQuery = `MATCH (p:Person)
                     WHERE p.name = $personName
                     RETURN p.name AS name`
    const readResult = await session.readTransaction(tx =>
        tx.run(readQuery, { personName: person1Name })
    )
    readResult.records.forEach(record => {
        console.log(`Found person: ${record.get('name')}`)
    })
}).then(result => {
    console.log("all done");
}).catch(err => {
    console.log(err);
});

这可以根据需要变得更加灵活或更具可扩展性,但显然总体思路是保持简单,以便通用代码的简单使用不需要大量代码。