AdvancingEnemy
AdvancingEnemy

Reputation: 422

why the Timer works so many times even I set its repeat = false?

I want to set a 800ms delay time to run a function , so I use a timer to handle it. The code is as following.But I found, at the first time, the function runs right, it just show only one console.log("here is console....."); , but when I click it again, it shows 2 consoles, and at the third click, it shows 3 consoles, and so on...

I cannot understand why this happens, can any friends explain it for me ?

import QtQuick 2.6
import QtQuick.Window 2.2

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    MouseArea {
        anchors.fill: parent
        onClicked: {
            delayTimer(800,function(){
                console.log("here is console.....");
                var t= Math.random();
                console.log(t);
            })

        }
    }



    Timer{
        id:dtimer
    }

    function delayTimer(delayTime, cb) {
        console.log("delayTimer is starting");
        dtimer.interval = delayTime;
        dtimer.repeat = false;
        dtimer.triggered.connect(cb);
        dtimer.start();
    }
}

after a few clicks, when I click it again, output is:

qml: delayTimer is starting
qml: here is console.....
qml: 0.27777099609375
qml: here is console.....
qml: 0.407012939453125
qml: here is console.....
qml: 0.60552978515625
qml: here is console.....
qml: 0.360107421875
qml: here is console.....
qml: 0.21942138671875
qml: here is console.....
qml: 0.252288818359375
qml: here is console.....
qml: 0.88134765625
qml: here is console.....
qml: 0.63092041015625
qml: here is console.....
qml: 0.5125732421875

Upvotes: 4

Views: 407

Answers (2)

derM
derM

Reputation: 13691

As @w1ck3dg0ph3r pointed out, you have one function bound to the signal multiple times. However, as QML assumes, all slots are executed at the same time, there is no guaranteed order, so I think, his solution might fail, if QML decides to first disconnect your function cb before it executes it.

There might be some precautions put in place, but they seem not to be too safe, as you can see in this example:

Button {
    onClicked: {
        clicked.connect(clicked)
        console.log('here')
    }
}

This results in a infinite loop on the first click, though it might be expected that the connection is only executed in the next run, and not already in the same run.

On the other hand this example:

Button {
    onClicked: {
        clicked.connect(function() {console.log('here'); clicked.connect(function() {console.log('there')})});
    }
}

Here you might expect, from what we learned above, that both functions are executed in the first run, and the output is here and there - which is not (at least on my machine) So the behavior when changing the bindings to a signal during the execution of those is not too well defined.

The better solution depends on your usecase. Do you want to have multiple functions bound to the signal, or always just the one?

I'd reccomend to store your function in a variable, and handle the disconnect in the onClicked-event:

Button {
    property var myCB
    onClicked: {
        delayTimer(delayTime, cb) {
            if (myCB) dtimer.triggered.disconnect(myCB)
            myCB = cb
            dtimer.triggered.connect(myCB)
            ...
        }
    }
}

Like this you are able to ensure that the disconnect is performed at the right time. However if the timer is restarted by some other source, the function is still bound. If you don't want this, you need to have the function it self to ensure the disconnection.

To provide a tailored solution, you would need to add more detail on what your usecase is, and what exactly you expect.

Upvotes: 1

w1ck3dg0ph3r
w1ck3dg0ph3r

Reputation: 1011

You're connecting your signal to a slot every time you call delayTimer(), so the connections accumulate and slots are invoked multiple times. I'm not familiar with qml/js, but you need to disconnect slot after timeout is triggered:

function delayTimer(delayTime, cb) {
    console.log("delayTimer is starting");
    dtimer.interval = delayTime;
    dtimer.repeat = false;
    dtimer.triggered.connect(cb);
    dtimer.triggered.connect(function(){
        dtimer.triggered.disconnect(cb);
    });
    dtimer.start();
}

Upvotes: 7

Related Questions