拆分进程以等待 NodeJS 上的事件

Posted

技术标签:

【中文标题】拆分进程以等待 NodeJS 上的事件【英文标题】:Split a process to wait for event on NodeJS 【发布时间】:2021-07-03 18:13:57 【问题描述】:

我正在编写一个使用 web3js 通过智能合约传输代币的方法。

当您启动传输事件时,您会得到 txHash,如果您想获取与传输相关的所有其他值,您必须订阅一个事件并等待它发生以获取值.

我必须将值返回给客户,所以我订阅了 transfer 事件并等待它广播返回数据。

问题是这可能需要很长时间(想想从 10 秒到几小时),有时它会给我一个超时,所以前端团队建议通知我一个 webhook 端点,当它发生时我将事件信息转发给它发生。

所以我必须把这个过程一分为二:

    进行传输并通知 txHash,并启动一个单独的进程 (2) 来监听事件。

    订阅事件,并在事件发生时将其转发到提供的 webhook。

我的代码现在看起来像这样:

function transfer(req, res, next) 

    try 

            contractRouter.transfer(from, to, tokenId).then(function(result)
                transferToWebhook(whHostname, whPath, result);
                next();
            ).fail(function(err)
                return res.status(500).json(status: 'error', name: err.name, message: err.message);
    
    catch (ex) 
        return res.status(500).json(status: 'error', name: ex.name, message: ex.message);
    

传输到 webhook 的函数如下所示:

function transferToWebhook(whHostname, whPath, txHash)
    contractRouter.getTransferEvent(txHash).then(function(result)

        var postData = JSON.stringify(result);
        var options = 
            hostname: whHostname,
            port: 80,
            path: whPath,
            method: 'POST',
            headers: 
                'Content-Type': 'application/json',
            
        
            var req = https.request(options, (res) => 
              console.log(`STATUS: $res.statusCode`);
              console.log(`HEADERS: $JSON.stringify(res.headers)`);
              res.setEncoding('utf8');
              res.on('data', (chunk) => 
                console.log(`BODY: $chunk`);
              );
              res.on('end', () => 
                console.log('No more data in response.');
              );
            );

            req.on('error', (e) => 
              console.log(`problem with request: $e.message`);
            );

            req.write(postData);
            req.end();
    );

订阅传输事件的函数如下所示:

function getTransferEvent(txHash)
    var deferred = q.defer();

    ethereumHandler.setContract(config.get(cABIAddress), cAddress).then(function(abiContract)

        Promise.resolve(txHash).then(function resolver(txHash)

            abiContract.getPastEvents('Transfer',filter: transactionHash: txHash, function(error, events))
            .then(function(event)
                var returnValues = 
                    from: event.returnValues.from,
                    to: event.returnValues.to,
                    tokenId: event.returnValues.tokenId
                
                deferred.resolve(returnValues);
            );
        );
    
    return deferred.promise;
    );

如果我将最后一个函数的代码直接放在传输函数上,则它的代码有效,但如果我尝试通过 transferToWebhook 函数调用它,则不会调用它。

应答第一个请求后如何启动 transferToWebhook 功能?

【问题讨论】:

【参考方案1】:

您可以使用 child_process 模块中的 spawn() 方法生成您的进程,然后监听事件 (process.on('data')) 并使用承诺来使用返回的数据。我不确定它是否能解决您的问题,因为您的函数是contractRouter contractRouter.getTransferEvent(txHash) 的对象,但您应该能够以某种方式对其进行调整。看一个例子来说明我的意思。

在你的 file.js 中

const  spawn  = require('child_process')

function transfertToWebHook(data) 
    getTransfertEvent(data)
        .then((result) => 
            const dts = JSON.parse(result)
            console.log('the res: ', dts)
            // send the res back
        )
        .catch(e => console.log('handle the err: ', e))
   
       console.log('carry on mother process')



function getTransfertEvent(data) 
    return new Promise((resolve, reject) => 
        const sp = spawn(process.execPath, ['childProcess.js'])
        // pass the arguments, here it will be the txHash
        sp.stdin.write(data)
        sp.stdin.end()
        sp.stdout.on('data', (d) => 
            // when datas get proceed you get it back.
            resolve(d.toString())
        )
        sp.stdout.on('error', (e) => 
            reject(e)
        )


        console.log('run what ever need to be proceed on the mother process')
    )

transfertToWebHook('test')

创建另一个文件名 childProcess.js。

使用Tranform流处理process.sdtin数据,然后通过process.stdout返回

const  Transform, pipeline  = require('stream')

const createT = () => 
    return new Transform(
        transform(chunk, enc, next) 
            // here run the code of you getTransfertEventFunction()
            
            // simulate some async process
            setTimeout(() => 
                // chunk is your passed arguments
                // !! chunk is a buffer so encode it as utf8 using 'toString()'
                // make it upperCase to simulate some changement
                const dt = chunk.toString().toUpperCase()

                // return an object as it's what your func return
                const proceededDatas = 
                                name: dt,
                                from: "from datas",
                                to: "to datas",
                                tokenId: "the token"
                        
                const dataString = JSON.stringify(proceededDatas)
                next(null, dataString)
            , 1000)
        
    )

pipeline(process.stdin, createT(), process.stdout, e => console.log(e))

运行代码:node file.js

【讨论】:

谢谢杰罗姆,我在理解如何通过标准输入传递 args 并在另一边读取它们时遇到了一些麻烦,但我想我可能能够调整我的代码以生成子进程在你的帮助下。如果您能在论据部分帮助我,将不胜感激。 NVM,我只是想通了,即使我想将对象作为 arg 传递,但似乎不可能。如果您对如何做有任何见解,请告诉我。感谢您的帮助! 是的,您可以将对象作为参数传递(这样您可以根据需要传递任意数量的参数/参数),您只需先将其字符串化,然后在转换流中解析它(它将在块变量中)并使用它(就像我从子进程回到母进程一样)。 请记住,子进程位于具有不同 PID 的新进程中,因此来自母进程的任何实例化连接都不会在子进程中可用。例如,如果您连接到 API 并从中获取实例,则无法将其获取/传输到子进程(但您可以传输所需的任何数据),连接必须在子进程中完成,然后返回数据然后可以返回到母进程。 你也可以在childProcess中放一些console.log()来调试,它们会出现在你的控制台中(因为母亲和孩子之间有一个开放的流连接)

以上是关于拆分进程以等待 NodeJS 上的事件的主要内容,如果未能解决你的问题,请参考以下文章

等待事件在 Node.js 进程内触发

NodeJS基础入门

Nodejs:将 Ctrl+C 发送到 Windows 上的子进程

nodejs进程线程优化性能

select函数总结

nodejs入门总结二:事件驱动