热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

js接收php回调,PHP实现类似javascript的setTimeout异步回调

在php中,我们偶尔会希望这样的场景:当执行某段程序的时候,需要执行某个任务,但是该任务并不需要立即执行,而是

在php中,我们偶尔会希望这样的场景:当执行某段程序的时候,需要执行某个任务,但是该任务并不需要立即执行,而是需要延迟几秒再执行。这个功能正如Javascript的setTimeout,但是php中并没有现成的异步功能,php的单线程的,所以要实现这个效果,我们还需要想想其他的办法。

不过幸运的是,php中提供了一种非阻塞的通信方式。因此,我们创建下面这个类:

class AsyncCallback {

private $auth;

public function __construct() {

$this->auth = substr(md5('iosnae23a9a~40^33fsdf.adsfie*'),8,16); // 修改内容,作为密钥

$headers = array();

foreach ($_SERVER as $key => $value) {

if('HTTP_' == substr($key,0,5)) {

$key = substr($key,5);

$key = strtolower($key);

$headers[$key] = $value;

}

}

$this->headers = $headers;

if(isset($this->headers['auth']) && $this->headers['auth'] == $this->auth) {

ignore_user_abort();

set_time_limit(0);

$event = $this->headers['event'];

$data = $this->headers['data'];

parse_str($data,$data);

call_user_func(array($this,$event.'Callback'),$data);

exit;

}

}

public function setTimeout($function,$timeout) {

$this->sock('setTimeout',array('function' => $function,'timeout' => $timeout));

}

private function setTimeoutCallback($data) {

$function = $data['function'];

$timeout = $data['timeout'];

sleep($timeout);

call_user_func($function);

}

private function sock($event,$data) {

$url = $this->current_url();

$host = parse_url($url,PHP_URL_HOST);

$path = parse_url($url,PHP_URL_PATH);

$query = parse_url($url,PHP_URL_QUERY);

if($query)

$path .= '?'.$query;

$port = parse_url($url,PHP_URL_PORT);

$port = $port ? $port : 80;

$scheme = parse_url($url,PHP_URL_SCHEME);

if($scheme == 'https')

$host = 'ssl://'.$host;

$data = http_build_query($data);

$fp = fsockopen($host,$port,$errno,$errstr,1);

if(!$fp) {

return false;

}

stream_set_blocking($fp,0);

stream_set_timeout($fp,1);

$header = "GET $path  / HTTP/1.1\r";

$header .= "Host: $host\r";

$header .= "Event: $event\r";

$header .= "Data: $data\r";

$header .= "Auth: {$this->auth}\r";

$header .= "Connection: Close\r\r";

fwrite($fp,$header);

fclose($fp);

return true;

}

private function current_url() {

$url = $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];

if(!isset($_SERVER['HTTPS']))

$url = 'http://'.$url;

elseif($_SERVER['HTTPS'] === 1 || $_SERVER['HTTPS'] === 'on' || $_SERVER['SERVER_PORT'] == 443)

$url = 'https://'.$url;

else

$url = 'http://'.$url;

return $url;

}

}

我们来看下具体的用法:

require 'AsyncCallback.class.php';

$AsyncCallback = new AsyncCallback();

file_put_contents('./runtime/setTimeout.txt','['.date('Y-m-d H:i:s').']['.microtime().'] 我先处理一点事情',LOCK_EX);

$AsyncCallback->setTimeout('setTimeout',10);

function setTimeout() {

file_put_contents('./runtime/setTimeout.txt',"".'['.date('Y-m-d H:i:s').']['.microtime().'] 我是延时执行的结果',FILE_APPEND);

}

file_put_contents('./runtime/setTimeout.txt',"".'['.date('Y-m-d H:i:s').']['.microtime().'] 我再处理一点事情,看看是否有时间差距',FILE_APPEND);

接下来我们来解释一下这里面都做了什么。

首先,sock()方法

我们来具体分析一下sock方法中fsockopen的用法。fsockopen开启了一个fsock通信,stream_set_blocking()函数的第二个参数设置为0,声明这个fsock为非阻塞模式,也就是说,下次在调用$this->sock()的时候,程序是流畅执行的,fsock发出请求时,不会阻塞进程,fsock发出请求后就继续往下执行,而无需得到结果。

fsock使用fwrite或fput发出请求,其中第二个参数需要我们自己构造http header信息,你需要了解http协议的东西,构造header可以帮我们实现get、post、head、put、delete等多种请求方式。

总之,sock()方法,帮我们实现非阻塞的发出请求。

其次,异步执行

我们来看下__construct()方法中的 if(isset($this->headers['auth']) && $this->headers['auth'] == $this->auth) 的内容。

ignore_user_abort();

set_time_limit(0);

这两句可以帮助程序在后台继续执行,即使访问被客户端关闭,也就是说fsock发出的非阻塞请求虽然并没有等结果返回,但是我们如此声明以后,程序仍然会继续执行,不会被中断。

call_user_func(array($this,$event.'Callback'),$data);

这句则是调用当前事件的回调函数(方法),这里就是$this->setTimeoutCallback(),而在setTimeoutCallback()中,使用了

call_user_func($function);

也就是调用我们第二段代码中规定的那个setTimeout()函数。

最后,回调函数

而这个setTimeout()函数就是回调函数,它会在fsock请求的终端进行执行,而它的执行将会开启一个新的php进程,不会影响当前的这个页面的程序的执行(虽然它是被写在当前页面内的)。

关于php的进程,请阅读《PHP进程分支设计》,了解php单线程的局限。

总之,你只需要在你的程序中引入AsyncCallback类,并且实例化后如下操作即可:

$AsyncCallback = new AsyncCallback();

$AsyncCallback->setTimeout('setTimeout',10);

function setTimeout() {    // 延时执行的代码

}

不过,本类的使用,也有一些缺陷:

和Javascript不同,该类不能在界面展示上实现异步展示,而只能实现任务上的异步

必须在页面开头的地方实例化类,因为回调是在类的构造方法中实现的

不能和主进程通信,这一点是最大的缺陷

2016-02-18

8242



推荐阅读
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • Monkey《大话移动——Android与iOS应用测试指南》的预购信息发布啦!
    Monkey《大话移动——Android与iOS应用测试指南》的预购信息已经发布,可以在京东和当当网进行预购。感谢几位大牛给出的书评,并呼吁大家的支持。明天京东的链接也将发布。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 本文详细介绍了PHP中与URL处理相关的三个函数:http_build_query、parse_str和查询字符串的解析。通过示例和语法说明,讲解了这些函数的使用方法和作用,帮助读者更好地理解和应用。 ... [详细]
  • Html5-Canvas实现简易的抽奖转盘效果
    本文介绍了如何使用Html5和Canvas标签来实现简易的抽奖转盘效果,同时使用了jQueryRotate.js旋转插件。文章中给出了主要的html和css代码,并展示了实现的基本效果。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
author-avatar
博饼薄饼
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有