Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 所有回调都同时执行,但为什么?_Javascript_Node.js - Fatal编程技术网

Javascript 所有回调都同时执行,但为什么?

Javascript 所有回调都同时执行,但为什么?,javascript,node.js,Javascript,Node.js,我目前正在包装一个用NodeJS制作的可执行文件。可执行文件可以保存字符串,以便在可执行文件中的其他进程中使用。每次可执行文件“保存”字符串时,它都会通过标准输出将指针发送回服务器。NodeJS服务器通过将字符串发送到可执行文件的stdin来保存字符串 最初我是这样写代码的: CLRProcess.stdout.once('data',function(strptr){ CLRProcess.stdout.once('data', function(str){ cons

我目前正在包装一个用NodeJS制作的可执行文件。可执行文件可以保存字符串,以便在可执行文件中的其他进程中使用。每次可执行文件“保存”字符串时,它都会通过标准输出将指针发送回服务器。NodeJS服务器通过将字符串发送到可执行文件的stdin来保存字符串

最初我是这样写代码的:

CLRProcess.stdout.once('data',function(strptr){
    CLRProcess.stdout.once('data', function(str){
         console.log(str.toString())
    })
    CLRProcess.stdin.write("StringReturn " + strptr.toString())
})
CLRProcess.stdin.write("StringInject __CrLf__ Mary had a__CrLf__little lamb.")
strPtr = Exec("StringInject __CrLf__ Mary had a__CrLf__little lamb.")
str = Exec("StringReturn " + strPtr)
// do stuff with str
上面的代码注入一个字符串

Mary had a
little lamb.
接收指向该字符串的指针,然后在下一步中通过将指针发送回主机应用程序来请求该字符串

为了简化编码算法,我需要这样一个系统:

CLRProcess.stdout.once('data',function(strptr){
    CLRProcess.stdout.once('data', function(str){
         console.log(str.toString())
    })
    CLRProcess.stdin.write("StringReturn " + strptr.toString())
})
CLRProcess.stdin.write("StringInject __CrLf__ Mary had a__CrLf__little lamb.")
strPtr = Exec("StringInject __CrLf__ Mary had a__CrLf__little lamb.")
str = Exec("StringReturn " + strPtr)
// do stuff with str
这是我写的代码:

class Pointer {
    constructor(){
        this.value = undefined
        this.type = "ptr"
    }
}


class CLR_Events extends Array {
    constructor(CLR){
        super()
        this.CLR = CLR
    }
    runAll(){
        if(this.length>0){
            //Contribution by le_m: https://stackoverflow.com/a/44447739/6302131. See Contrib#1
            this.shift().run(this.runAll.bind(this))
        }
    }
    new(cmd,args,ret){
        var requireRun = !(this.length>0)   //If events array is initially empty, a run is required
        var e = new CLR_Event(cmd,args,ret,this.CLR)
        this.push(e)
        if(requireRun){
            this.runAll()
        }
    }
}

class CLR_Event {
    constructor(cmd,args,ret,CLR){
        this.command = cmd;
        this.args = args
        this.CLR = CLR
        this.proc = CLR.CLRProcess;
        this.ptr = ret
    }

    run(callback){
        //Implementing event to execute callback after some other events have been created.
        if(this.command == "Finally"){
            this.args[0]()
            console.log("Running Finally")
            return callback(null)
        }

        //Implementation for all CLR events.
        var thisEvent = this
        this.proc.stdout.once('data',function(data){
            this.read()
            data = JSON.parse(data.toString())
            thisEvent.ptr.value = data
            callback(data);
        })
        this.proc.stdin.write(this.command + " " + this._getArgValues(this.args).join(" ") + "\n");
    }
    _getArgValues(args){
        var newArgs = []
        this.args.forEach(
            function(arg){
                if(arg.type=='ptr'){
                    if(typeof arg.value == "object"){
                        newArgs.push(JSON.stringify(arg.value))
                    } else {
                        newArgs.push(arg.value)
                    }
                } else if(typeof arg == "object"){
                    newArgs.push(JSON.stringify(arg))
                } else {
                    newArgs.push(arg)
                }
            }
        )
        return newArgs  
    }
}

var CLR = {}
CLR.CLRProcess = require('child_process').spawn('DynaCLR.exe')
CLR.CLRProcess.stdout.once('data',function(data){
    if(data!="Ready for input."){
        CLR.CLRProcess.kill()
        CLR = undefined
        throw new Error("Cannot create CLR process")
    } else {
        console.log('CLR is ready for input...')
    }
})
CLR.Events = new CLR_Events(CLR)

//UDFs

CLR.StringInject = function(str,CrLf="__CLR-CrLf__"){
    var ptr = new Pointer
    this.Events.new("StringInject",[CrLf,str.replace(/\n/g,CrLf)],ptr) //Note CLR.exe requires arguments to be the other way round -- easier command line passing
    return ptr
}
CLR.StringReturn = function(ptr){
    var sRet = new Pointer
    this.Events.new("StringReturn",[ptr],sRet)
    return sRet
}

CLR.Finally = function(callback){
    this.Events.new("Finally",[callback])
}
我打算这样做:

  • 函数
    StringInject
    StringReturn
    最终创建事件并将它们附加到
    事件
    数组中
  • Events
    对象的
    runAll()
    函数从其数组中删除第一个“event”,并运行数组的
    run()
    函数,将自身作为回调传递
  • run函数写入可执行文件的stdin,等待stdout中的响应,将数据附加到传入的指针,然后执行传递给它的
    runAll()
    函数
  • 这是我不明白的。。。执行多个字符串注入时:

    S_ptr_1 = CLR.StringInject("Hello world!")
    S_ptr_2 = CLR.StringInject("Hello world!__CLR-CrLf__My name is Sancarn!")
    S_ptr_3 = CLR.StringInject("Mary had a little lamb;And it's name was Doug!",";")
    
    我得到以下数据:

    S_ptr_1 = {value:123,type:'ptr'}
    S_ptr_2 = {value:123,type:'ptr'}
    S_ptr_3 = {value:123,type:'ptr'}
    
    其中,数据应为:

    S_ptr_1 = {value:1,type:'ptr'}
    S_ptr_2 = {value:2,type:'ptr'}
    S_ptr_3 = {value:3,type:'ptr'}
    
    我能想到的唯一情况是,在伪代码中,如果发生以下情况:

    CLRProcess.stdin.write("StringInject Val1")
    CLRProcess.stdin.write("StringInject Val2")
    CLRProcess.stdin.write("StringInject Val3")
    CLRProcess.stdout.once('data') ==> S_ptr_1
    CLRProcess.stdout.once('data') ==> S_ptr_2
    CLRProcess.stdout.once('data') ==> S_ptr_3
    

    但是为什么呢?是我忽略了什么,还是这个算法有根本性的问题?

    我发现了问题,并设法找到了解决方案

    问题 调用
    CLR.StringInject
    ,调用
    CLR\u Events.new()
    CLR\u Events.new()
    首先检查数组是否为空或是否有事件,创建新事件并将事件推送到事件数组。如果数组最初为空,则调用runAll()

    CLR\u Events.runAll()

    然后运行下一行代码:

    调用
    CLR.StringInject
    ,调用
    CLR\u Events.new()
    CLR\u Events.new()
    ,首先检查数组是否为空或是否有事件,它看到数组为空,因此调用runAll()

    这就是问题所在。

    runAll()
    将调用自身来调用事件数组中的下一个事件。但是
    Events
    数组始终为空,因为
    CLR\u Events.new()
    不知道如何检查当前是否正在执行事件。它只检查数组中是否有事件。现在我们已经向STDIN写了两次,并在STDOUT上设置了两个侦听器。这将链接到第三个命令,最终意味着所有返回的对象都包含相同的数据


    解决方案 为了解决这个问题,我必须创建一个
    this.isRunning
    变量

    仅当isRunning==false时才应调用
    RunAll()

    isRunning
    仅当以下两项均为真时才应为假:

  • 当前没有正在执行的事件调用
  • Events.length==0
  • 即,
    isRunning
    应在回调后设置为false

    这确保所有事件都在同一个回调循环中触发

    然后,我在回调时遇到了一些其他问题,因为
    未定义的

    function(){
        this.runAll.bind(this)()
        if (!(this.length>0))  this.isRunning = false
    }
    
    为了解决这个问题,我必须在回调定义之前添加一个
    CLREvents
    变量来存储
    this
    ,并在回调中用
    CLREvents
    替换
    this
    。现在它终于按预期工作了

    完整工作代码:

    class Pointer {
        constructor(){
            this.value = undefined
            this.type = "ptr"
        }
    }
    
    class CLR_Events extends Array {
        constructor(CLR){
            super()
            this.CLR = CLR
            this.isRunning = false
        }
        runAll(){
            console.log('RunAll')
            this.isRunning = true
            if(this.length>0){
                //Contribution by le_m: https://stackoverflow.com/a/44447739/6302131. See Contrib#1
                var CLREvents = this
                this.shift().run(function(){
                    CLREvents.runAll.bind(CLREvents)()
                    if (!(CLREvents.length>0))  CLREvents.isRunning = false
                })
            }
        }
        new(cmd,args,ret){
            console.log("New Event: " + JSON.stringify([cmd,args,ret]) + " - requireRun:" + (!(this.length>0)).toString())
            //If events array is initially empty, a run is required
            var requireRun = !(this.length>0)
    
            var e = new CLR_Event(cmd,args,ret,this.CLR)
            this.push(e)
            if(!this.isRunning){
                this.runAll()
            }
        }
    }
    
    class CLR_Event {
        constructor(cmd,args,ret,CLR){
            this.command = cmd;
            this.args = args
            this.CLR = CLR
            this.proc = CLR.CLRProcess;
            this.ptr = ret
        }
    
        run(callback){
            console.log("RunOne")
            //Implementing event to execute callback after some other events have been created.
            if(this.command == "Finally"){
                this.args[0]()
                console.log("Running Finally")
                return callback(null)
            }
    
            //Implementation for all CLR events.
            var thisEvent = this
            this.proc.stdout.once('data',function(data){
                console.log('Callback')
                this.read()
                data = JSON.parse(data.toString())
                thisEvent.ptr.value = data
                callback(data);
            })
            this.proc.stdin.write(this.command + " " + this._getArgValues(this.args).join(" ") + "\n");
        }
        _getArgValues(args){
            var newArgs = []
            this.args.forEach(
                function(arg){
                    if(arg.type=='ptr'){
                        if(typeof arg.value == "object"){
                            newArgs.push(JSON.stringify(arg.value))
                        } else {
                            newArgs.push(arg.value)
                        }
                    } else if(typeof arg == "object"){
                        newArgs.push(JSON.stringify(arg))
                    } else {
                        newArgs.push(arg)
                    }
                }
            )
            return newArgs  
        }
    }
    
    var CLR = {}
    CLR.CLRProcess = require('child_process').spawn('DynaCLR.exe')
    CLR.CLRProcess.stdout.once('data',function(data){
        if(data!="Ready for input."){
            CLR.CLRProcess.kill()
            CLR = undefined
            throw new Error("Cannot create CLR process")
        } else {
            console.log('CLR is ready for input...\n')
            /* Example 1 - Using String Inject */
            S_ptr_1 = CLR.StringInject("Hello world!")
            S_ptr_2 = CLR.StringInject("Hello world!__CLR-CrLf__My name is Sancarn!")
            S_ptr_3 = CLR.StringInject("Mary had a little lamb;And it's name was Doug!",";")
            console.log(S_ptr_1)
            console.log(S_ptr_2)
            console.log(S_ptr_3)
        }
    })
    CLR.Events = new CLR_Events(CLR)
    
    //UDFs
    
    CLR.StringInject = function(str,CrLf="__CLR-CrLf__"){
        var ptr = new Pointer
        this.Events.new("StringInject",[CrLf,str.replace(/\n/g,CrLf)],ptr) //Note CLR.exe requires arguments to be the other way round -- easier command line passing
        return ptr
    }
    CLR.StringReturn = function(ptr){
        var sRet = new Pointer
        this.Events.new("StringReturn",[ptr],sRet)
        return sRet
    }
    
    CLR.Finally = function(callback){
        this.Events.new("Finally",[callback])
    }
    

    听起来您忽略了
    write
    是异步的这一事实。另外,
    新指针
    不会创建对象。您需要调用构造函数:
    new Pointer()
    您只有一个CLR对象,因此只有一个DynaCLR.exe,因此每个stdin和stdout流只有一个。如果要为每个调用创建一个新的DynaCLR,您可能会获得更大的成功。@GregHNZ Yes。要求只有1个标准输入/标准输出流。所有内容都应该由相同的DynaCLR可执行文件保存,因为我将使用它来编译VB和C#源代码。将它们存储在多个不同的进程中将不允许这种可能性(不存储物理文件,这对我来说是不可能的)。使用stdin/out只是IPC的一种手段。@slebetman但这让我很困惑。。。当然,当我收到stdout时,我会调用下一个
    CLR\u事件。只有在我写信给stdin之后我才能收到stdout。。。所以我应该同时等待stdin和stdout的异步时间。。。?关于
    new Pointer()
    的有趣内容。。。这不是我在REPL中看到的。那是odd@slebetman在指针处,你看,可能只是遗漏了一些东西。