Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

JS Closures in Signal handlers



  • Using Qt Quick 2.1, after investing a good amount of time in debugging a refactor to some qml code I've noted a bug triggered by a nuanced behavior of how closures are constructed in signal handlers

    I have some code that looks like the following:

    // QML user defined component Foo.qml
    BaseUserType {
        //...
        signal somethingHappened(string description)
        //...
    }
    
    // QML user defined component BarOne.qml
    Foo {
        onSomethingHappened: {
            console.log(description)
        }
    }
    

    BarOne.qml appears to work fine. When the signal somethingHappened("blah") is emitted, the handler executes and prints "blah".

    // QML user defined component BarTwo.qml
    Foo {
        function processSomethingHappened(callback) {
            //... do other stuff first
            if (callback) {
                callback()
            }
        }
    
        onSomethingHappened: {
            processSomethingHappened(function() {
                console.log(description)
            })
        }
    }
    

    BarTwo.qml does not properly function. When the signal somethingHappened("blah") is emitted, the handler executes and description is undefined. My take away was that the variable 'description' was not captured in the JS closure of my anonymous function, which had me questioning if closures work different in QML's JS subset. The last test really threw me off though...

    // QML user defined component BarThree.qml
    Foo {
        function processSomethingHappened(callback, argument) {
            //... do other stuff first
            if (callback) {
                callback(argument)
            }
        }
    
        onSomethingHappened: {
            var tmp = description
            processSomethingHappened(
                function(argument) {
                    console.log(description)
                    console.log(argument)
                    console.log(tmp)
                },
                tmp
            )
        }
    }
    

    On emission of somethingHappened("blah") in this case I am seeing all 3 items (including 'description') print "blah" which means that where 'description' wasn't captured in the closure of the previous example, it is in this case, presumably because the QtQuick engine had to evaluate the argument in the original closure of the signal handler or something.

    Is this a bug in QML? If so, is it corrected in newer versions? If not, is there documentation somewhere explaining the nuance of how closures are binded in QML's subset of JS?


Log in to reply