0
$msg_sent = false;
foreach ($channels as $channel) {
    $resolve = function() use ( &$msg_sent )
    {
        $msg_sent = true;
    };
    $reject = function( \Exception $e )
    {
        error_log( $e, 3, './error.txt' . PHP_EOL );
    };
    $channel->send('hlw')->done( $resolve, $reject );
    if ( $msg_sent ){
        break;
    } else {
        continue;
    }
}

As you can see above,
$msg_sent is false,
$channels is a array with 3 instance of same objects(have different values)
and when i tap send(), it returns a ExtendedPromiseInterface, in which $resolve is executed when message is sent and $reject when not sent.

So what i want to do is check if the message is sent or not, if not then continue the loop and try to send message to another channel, if sent then break the loop.

But unexpectedly it always return false and the loop runs even if the message is sent.

  • 1
    To answer this question it'd be crucial to know how the channels you're using are implemented. Considering that `$msg_sent` always equals `false` and it's about communcation (channels), they possibly work in a threaded, asynchronous way which runs in parallel to the loop (leading to all kinds of unexpected results, like your one). – Loilo Jun 13 '18 at 12:52
  • @Loilo yeah it implements 2 of traits and 1 of interface, fyi its a library https://github.com/CharlotteDunois/Yasmin documentation of channel https://yasmin.neko.run/master/CharlotteDunois/Yasmin/Models/TextChannel.html –  Jun 13 '18 at 12:56
  • So, regarding the introduction section of that project... "Before you start using this Library, you need to know [...] how Event Loops and Promises work." *Do* you know how Event Loops and Promises work? :) – Loilo Jun 13 '18 at 12:59
  • yes... already knew it... –  Jun 13 '18 at 13:01
  • So I guess you also know what the overall problem probably is then: the `->done()` callback is not executed synchronously, i.e. not during the execution of the `for` loop. Sorry, in a lack of experience with async PHP I can't help you any further, just wanted to make sure you're familiar with the general concepts of asynchronous programming. – Loilo Jun 13 '18 at 13:05
  • The callback is executed synchronously, but the issue is that the `send` method is executed asynchronously (thus returning a promise). Thus the loop might end before the promise even resolves. To have this functionality work, recursion has to be used. – Charlotte Dunois Jun 13 '18 at 13:17
  • But the whole issue is, that it's the wrong way to solve problem X. Instead permissions should be checked before trying to send. – Charlotte Dunois Jun 13 '18 at 13:18

1 Answers1

0

Hey ReactPHP team member here. That foreach loop will just send everything at (nearly) the same time. You could do something like this to only send a request after you receive the previous one with this (pseudo code), which resolves with true on success and false when all calls fail:

final class Bier
{
    public function check($channels): PromiseInterface
    {
        $channel = array_shift($channels);
        return $channel->send('hlw')->then(function () {
            return resolve(true);
        }, function () use ($channels) {
            if (count($channels) === 0) {
                return resolve(false);
            }
            return resolve($this->check($channels));
        });
    }
}
WyriHaximus
  • 1,000
  • 6
  • 8
  • That's pretty much what I meant by recursion. But for clear distinction between success and failure I would reject on failure. Returning a boolean is quite a bad habit in PHP. Doesn't solve the underlying problem that you shouldn't actively test sending through trial & error and instead check permissions. – Charlotte Dunois Jun 14 '18 at 04:47