php循环套循环
PHP developers are always waiting for something. Sometimes we’re waiting for requests to remote services. Sometimes we’re waiting for databases to return rows from a complex query. Wouldn’t it be great if we could do other things during all that waiting?
PHP开发人员总是在等待某些东西。 有时我们正在等待对远程服务的请求。 有时我们在等待数据库从复杂的查询返回行。 如果我们能在所有等待中做其他事情不是很好吗?
If you’ve written some JS, you’re probably familiar with callbacks and DOM events. And though we have callbacks in PHP, they don’t work in quite the same way. That’s thanks to a feature called the event loop.
如果您已经编写了一些JS,则可能熟悉回调和DOM事件。 尽管我们在PHP中有回调,但它们的工作方式并不完全相同。 这要归功于称为事件循环的功能。
![Pfeil1105a](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2015/09/1441736572Fotolia_83108550_Subscription_Monthly_M-1024x774.jpg)
We’re going to look at how the event loop works, and how we can use the event loop in PHP.
我们将研究事件循环如何工作,以及如何在PHP中使用事件循环。
We’re going to see some interesting PHP libraries. Some would consider these not yet stable enough to use in production. Some would consider the examples presented as “better to do in more mature languages”. There are good reasons to try these things. There are also good reasons to avoid these things in production. The purpose of this post is to highlight what’s possible in PHP.
我们将看到一些有趣PHP库。 有些人会认为这些还不够稳定,无法在生产中使用。 有些人会认为这些示例“更好地使用更成熟的语言”。 有充分的理由尝试这些事情。 也有充分的理由避免在生产中使用这些方法。 这篇文章的目的是强调PHP中的功能。
等待的地方 (Where Things Go To Wait)
To understand event loops, let’s look at how they work in the browser. Take a look at this example:
要了解事件循环,让我们看一下事件循环在浏览器中的工作方式。 看一下这个例子:
function fitToScreen(selector) {
var element = document.querySelector(selector);
var width = element.offsetWidth;
var height = element.offsetHeight;
var top = "-" + (height / 2) + "px";
var left = "-" + (width / 2) + "px";
var ratio = getRatio(width, height);
setStyles(element, {
"position": "absolute",
"left": "50%",
"top": "50%",
"margin": top + " 0 0 " + left,
"transform": "scale(" + ratio + ", " + ratio + ")"
});
}
function getRatio(width, height) {
return Math.min(
document.body.offsetWidth / width,
document.body.offsetHeight / height
);
}
function setStyles(element, styles) {
for (var key in styles) {
if (element.style.hasOwnProperty(key)) {
element.style[key] = styles[key];
}
}
}
fitToScreen(".welcome-screen");
This code requires no extra libraries. It will work in any browser that supports CSS scale transformations. A recent version of Chrome should be all you need. Just make sure the CSS selector matches an element in your document.
此代码不需要额外的库。 它可以在任何支持CSS缩放转换的浏览器中使用。 您需要的只是Chrome的最新版本。 只需确保CSS选择器与文档中的元素匹配即可。
These few functions take a CSS selector and center and scale the element to fit the screen. What would happen if we threw an Error
inside that for
loop? We’d see something like this…
这几个功能采用CSS选择器并居中并缩放元素以适合屏幕。 如果我们在该for
循环中抛出Error
,将会发生什么? 我们会看到这样的东西……
![stack-error](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2015/09/1441736680stack-error-1024x231.png)
We call that list of functions a stack trace. It’s what things look like inside the stack browsers use. They’ll handle this code in steps…
我们将该函数列表称为堆栈跟踪。 这就是浏览器内部使用的堆栈外观。 他们将逐步处理此代码…
![stack-animation](https://i-blog.csdnimg.cn/blog_migrate/932c84995b32958da0f138e349ddafe7.gif)
This is like how PHP uses a stack to store context. Browsers go a step further and provide WebAPIs for things like DOM events and Ajax callbacks. In its natural state, JavaScript is every bit as asynchronous as PHP. That is: while both look like they can do many things at once, they are single threaded. They can only do one thing at a time.
这就像PHP如何使用堆栈来存储上下文。 浏览器更进一步,为诸如DOM事件和Ajax回调之类的事情提供WebAPI。 在自然状态下,JavaScript与PHP一样异步。 那就是:虽然看起来它们都可以一次完成很多事情,但是它们都是单线程的。 他们一次只能做一件事。
With the browser WebAPIs (things like setTimeout and addEventListener) we can offload parallel work to different threads. When those events happen, browsers add callbacks to a callback queue. When the stack is next empty, browses pick the callbacks up from the callback queue and execute them.
使用浏览器WebAPI(诸如setTimeout和addEventListener之类的东西),我们可以将并行工作卸载到不同的线程。 当这些事件发生时,浏览器会将回调添加到回调队列中。 当堆栈下一个为空时,浏览器从回调队列中选择回调并执行它们。
This process of clearing the stack, and then the callback queue, is the event loop.
清除堆栈,然后清除回调队列的过程是事件循环。
没有事件循环的生活 (Life Without An Event Loop)
In JS, we can run the following code:
在JS中,我们可以运行以下代码:
setTimeout(function() {
console.log("inside the timeout");
}, 1);
console.log("outside the timeout");
When we run this code, we see outside the timeout
and then inside the timeout
in the console. The setTimeout
function is part of the WebAPIs that browsers give us to work with. When 1 millisecond has passed, they add the callback to the callback queue.
运行此代码时,我们会outside the timeout
inside the timeout
看到,然后inside the timeout
控制台中看到outside the timeout
。 setTimeout
函数是浏览器提供给我们使用的WebAPI的一部分。 1毫秒过去后,他们将回调添加到回调队列中。
The second console.log
completes before the one from inside the setTimeout
starts. We don’t have anything like setTimeout
in standard PHP, but if we had to try and simulate it:
第二个console.log
在setTimeout
内部的一个console.log
开始之前完成。 我们在标准PHP中没有类似setTimeout
东西,但是如果我们不得不尝试对其进行仿真:
function setTimeout(callable $callback, $delay) {
$now = microtime(true);
while (true) {
if (microtime(true) - $now > $delay) {
$callback();
return;
}
}
}
setTimeout(function() {
print "inside the timeout";
}, 1);
print "outside the timeout";
When we run this, we see inside the timeout
and then outside the timeout
. That’s because we have to use an infinite loop inside our setTimeout
function to execute the callback after a delay.
运行此命令时,我们会inside the timeout
看到,然后outside the timeout
。 这是因为我们必须在setTimeout
函数内使用无限循环才能在延迟后执行回调。
It may be tempting to move the while
loop outside of setTimeout
and wrap all our code in it. That might make our code feel less blocking, but at some point we’re always going to be blocked by that loop. At some point we’re going to see how we can’t do more than a single thing in a single thread at a time.
将while
循环移到setTimeout
外部并将所有代码包装在其中可能很诱人。 这可能会使我们的代码减少阻塞,但是在某些时候,我们总是会被该循环阻塞。 在某个时候,我们将看到如何一次只能在单个线程中完成一件事情。
While there is nothing like setTimeout
in standard PHP, there are some obscure ways to implement non-blocking code alongside event loops. We can use functions like stream_select
to create non-blocking network IO. We can use C extensions like EIO to create non-blocking filesystem code. Let’s take a look at libraries built on these obscure methods…
尽管在标准PHP中没有类似setTimeout
东西,但是有一些晦涩的方法可以在事件循环旁边实现非阻塞代码。 我们可以使用类似stream_select
功能来创建非阻塞网络IO。 我们可以使用EIO之类的C扩展来创建非阻塞文件系统代码。 让我们看一下基于这些晦涩方法的库…
冰柱 (Icicle)
Icicle is library of components built with the event loop in mind. Let’s look at a simple example:
冰柱是考虑到事件循环而构建的组件库。 让我们看一个简单的例子:
use Icicle\Loop;
Loop\timer(0.1, function() {
print "inside timer";
});
print "outside timer";
Loop\run();
This is with icicleio/icicle
version 0.8.0
.
这是icicleio/icicle
版本0.8.0
版本。
Icicle’s event loop implementation is great. It has many other impressive features; like A+ promises, socket, and server implementations.
冰柱的事件循环实现很棒。 它具有许多其他令人印象深刻的功能; 如A + promise,套接字和服务器实现。
Icicle also uses generators as co-routines. Generators and co-routines are a different topic, but the code they allow is beautiful:
冰柱还使用生成器作为协同例程。 生成器和协同例程是不同的主题,但是它们所允许的代码很漂亮:
use Icicle\Coroutine;
use Icicle\Dns\Resolver\Resolver;
use Icicle\Loop;
$coroutine = Coroutine\create(function ($query, $timeout = 1) {
$resolver = new Resolver();
$ips = (yield $resolver->resolve(
$query, ["timeout" => $timeout]
));
foreach ($ips as $ip) {
print "ip: {$ip}\n";
}
}, "sitepoint.com");
Loop\run();
This is with icicleio/dns
version 0.5.0
.
这是icicleio/dns
版本0.5.0
。
Generators make it easier to write asynchronous code in a way that resembles synchronous code. When combined with promises and an event loop, they lead to great non-blocking code like this!
生成器使它更容易以类似于同步代码的方式编写异步代码。 当与promise和事件循环结合使用时,它们会导致像这样的出色的非阻塞代码!
ReactPHP (ReactPHP)
ReactPHP has a similar event loop implementation, but without all the interesting generator stuff:
ReactPHP具有类似的事件循环实现,但没有所有有趣的生成器内容:
$loop = React\EventLoop\Factory::create();
$loop->addTimer(0.1, function () {
print "inside timer";
});
print "outside timer";
$loop->run();
This is with react/event-loop
version 0.4.1
.
这是带有react/event-loop
版本0.4.1
版本。
ReactPHP is more mature than Icicle, and it has a larger range of components. Icicle has a way to go before it can contend with all the functionality ReactPHP offers. The developers are making good progress, though!
ReactPHP比Icicle更成熟,并且具有更大范围的组件。 冰柱要想与ReactPHP提供的所有功能抗衡,还有一段路要走。 不过,开发人员正在取得良好进展!
结论 (Conclusion)
It’s difficult to get out of the single-threaded mindset that we are taught to have. We just don’t know the limits of code we could write if we had access to non-blocking APIs and event loops.
很难摆脱我们被教导拥有的单线程思维方式。 如果我们可以访问非阻塞API和事件循环,我们只是不知道可以编写的代码限制。
The PHP community needs to become aware of this kind of architecture. We need to learn and experiment with asynchronous and parallel execution. We need to pirate these concepts and best-practices from other languages who’ve had event loops for ages, until “how can I use the most system resources, efficiently?” is an easy question to answer with PHP.
PHP社区需要意识到这种架构。 我们需要学习和尝试异步和并行执行。 我们需要从具有事件循环年龄的其他语言中盗用这些概念和最佳实践,直到“我如何才能有效地利用最多的系统资源?” 用PHP回答一个简单的问题。
Stay tuned for a more practical implementation of Icicle, coming soon!
请继续关注Icicle的更实际实现,即将推出!
翻译自: https://www.sitepoint.com/an-introduction-into-event-loops-in-php/
php循环套循环