Perl:在(可能改变)时间延迟后重复调用子程序

Perl: repeatedly call subroutine after (possibly changing) time delay

好吧,我被难住了。这是我似乎无法解决的问题:在调用 public API 时,首先需要的是访问令牌——让我们调用我的方法 get_access_token()。获得访问令牌后,我将使用它从 public API 获取数据。现在,令牌在一定秒数后过期,该数字与访问令牌一起提供。所以,我的想法是设置某种计时器,一旦前一个令牌过期,它将通过再次调用 get_access_token() 来刷新访问令牌,这样我就可以继续从 API 获取数据,始终使用有效的访问令牌。

我试过使用:

但我无法使其中任何一个发挥作用。我不拘泥于任何特定的方法;我知道我可以使用 time() 和算术通过简单的 "check every so often to see if the access token is about to expire" 来解决它,但我想我会尝试一些更复杂的方法。显然对我来说太复杂了!

是的,我知道通常只有一个 post 的代码,但显然我的代码都是垃圾,所以我真的只是在寻找一些关于如何最好地处理这个特定用例的指示。任何帮助将不胜感激。

编辑

对于@ThisSuitIsBlackNot,这里是警报测试(没有实际的 API 调用)。警报触发一次但再也不会触发:

#!/usr/bin/perl

use 5.010;
use strict;
use warnings;

use Time::HiRes;

my $timeout = 7;
my $counter = 0;

my $rv = wrapper();
say $rv;

exit;


### subroutines

sub getAccessToken {
    return localtime(time());
}

sub getSomeIDs {
    my $accessToken = shift;

    say "getting IDs: timeout == $timeout";

    eval {
        local $SIG{'ALRM'} = sub { wrapper(); };
        alarm($timeout);
        while (1) {
            if ($counter == 25) { return; }
            say "[ $accessToken ] $counter";
            $counter = $counter + 1;
            alarm_resistant_sleep(1);
        }
        alarm(0);
    };
    alarm(0);

    return "counter at $counter";
}

sub wrapper {
    my $at = getAccessToken();
    return getSomeIDs($at);
}

sub alarm_resistant_sleep {
    my $end = Time::HiRes::time() + shift();
    for (;;) {
        my $delta = $end - Time::HiRes::time();
        last if $delta <= 0;
        select(undef, undef, undef, $delta);
    }
}

下面是使用 AnyEvent 的尝试。最初我没有使用闭包的东西,但阅读了另一个建议这样做的 post(仍然没有用):

#!/usr/bin/perl

use 5.010;
use strict;
use warnings;

use AnyEvent;

my $accessToken;
my $timeout = 7;
my $cv = AE::cv;

my $counter = 0;

my $f1 = makeClosure(\&refreshAccessToken);
my $f2 = makeClosure(\&getSomeIDs);

$f1->();
$f2->();
$cv->recv();

exit;


### subroutines

sub makeClosure {
    my $sub = shift;
    return $sub;
}

sub getAccessToken {
    $accessToken = localtime(time());
}

sub refreshAccessToken {
    my $t1; $t1 = AE::timer 0, $timeout,
        sub {
            say "callback called";
            $timeout--;
            $accessToken = localtime(time());
            #getAccessToken();
            undef $t1;
        };
    say "calling refreshAccessToken()";
}

sub getSomeIDs {
    my $t2; $t2 = AE::timer 0, 1,
        sub {
            if ($counter >= 16) { undef $t2; }
            say "[ $accessToken ] $counter";
            $counter = $counter + 1;
        };
    say "getting IDs: timeout == $timeout";
}

最后,这是 Time::HiRes 测试。同样,此代码经历了许多修改以使其正常工作。针对 $counter 测试的荒谬值只是为了查看是否在任何时候调用了计时器,但事实并非如此:

#!/usr/bin/perl

use 5.010;
use strict;
use warnings;

use Time::HiRes qw(setitimer ITIMER_VIRTUAL time);

my $accessToken;

my $counter = 0;

$SIG{VTALRM} = \&refreshAccessToken;
setitimer(ITIMER_VIRTUAL, 1, 5);

Time::HiRes::sleep(2);

getAccessToken();
getSomeIDs();

exit;


### subroutines

sub refreshAccessToken {
    my $timeout = getAccessToken();
    local $SIG{VTALRM} = \&refreshAccessToken;
    setitimer(ITIMER_VIRTUAL, $timeout, $timeout);
    die;
}

sub getAccessToken {
    say "getting access token";
    $accessToken = localtime(time());
    return 2;
}

sub getSomeIDs {

    my $loop = 1;

    #say "getting IDs: timeout == $timeout";

    while ($loop) {
        if ($counter >= 1600000000) { $loop = 0; }
        say "[ $accessToken ] $counter";
        $counter = $counter + 1;
    };
}

如果过期时间由 API 给出,这很容易解决。这是一种使用 time 和简单算术的方法。

sub get_access_token {
    # some token generation code
    return ( $token, $expire_time );
}

my ( $token, $expire_time ) = get_access_token;
my $expire_timestamp = time + $expire_time;

while (1) {
    if ( time > $expire_timestamp ) {
        ( $token, $expire_time ) = get_access_token;
        $expire_timestamp = time + $expire_time;
    }

    # make requests using $token
}

我不太熟悉 alarm,但我的尝试(根据文档)将如下所示,即设置一个 SIGALRM 处理程序来调用您的 get_access_token(),这将更新您的 token_expire_timing然后再次将闹钟设置为 token_expire_timing

my ( $token, $expire_time ) = get_access_token();

eval {
    local $SIG{ALRM} = sub {
        ( $token, $expire_time ) = get_access_token();
    };
    alarm $expire_time;    # schedule alarm in $expire_time
    while (1) {
        # your API requests here with $token
    }
    alarm 0; #cancel the alarm
};
if ($@) {
    die "$@\n"; #propagate some other errors
}

sub get_access_token {

    # some token generation code
    ( $token, $expire_time );
}

好的,所以 time() 和算术它是;可爱消失了 window。感谢所有提出建议的人:

#!/usr/bin/perl

use 5.010;
use strict;
use warnings;

my $counter = 0;

my ($token, $expire_time, $expire_ts) = get_access_token();

my $loop = 1;
while ($loop) {
    if (time() > $expire_ts) {
        ($token, $expire_time, $expire_ts) = get_access_token();
    }
    if ($counter == 14) { $loop = 0; }
    say "[ $token ] $counter";
    $counter = $counter + 1;
    sleep(1);
}

exit;


### subroutines

sub get_access_token {
    my $token = localtime(time());
    my $expire_time = 3;
    my $expire_ts = time() + $expire_time;
    # some token generation code
    return ( $token, $expire_time, $expire_ts );
}

输出:

$ ./timeTest.pl
[ Fri Feb 26 17:05:46 2016 ] 0
[ Fri Feb 26 17:05:46 2016 ] 1
[ Fri Feb 26 17:05:46 2016 ] 2
[ Fri Feb 26 17:05:46 2016 ] 3
[ Fri Feb 26 17:05:50 2016 ] 4
[ Fri Feb 26 17:05:50 2016 ] 5
[ Fri Feb 26 17:05:50 2016 ] 6
[ Fri Feb 26 17:05:50 2016 ] 7
[ Fri Feb 26 17:05:54 2016 ] 8
[ Fri Feb 26 17:05:54 2016 ] 9
[ Fri Feb 26 17:05:54 2016 ] 10
[ Fri Feb 26 17:05:54 2016 ] 11
[ Fri Feb 26 17:05:58 2016 ] 12
[ Fri Feb 26 17:05:58 2016 ] 13
[ Fri Feb 26 17:05:58 2016 ] 14