菜单

浅析php如何实现App常用的秒发功能_php实例_脚本之家

2019年11月26日 - 金沙国际唯一官网

前言

Ajax和XMLHttpRequest

我们通常将Ajax等同于XMLHttpRequest,但细究起来它们两个是属于不同维度的2个概念。

以下是我认为对Ajax较为准确的解释:(摘自what is
Ajax)

AJAX stands for Asynchronous JavaScript and XML. AJAX is a new technique
for creating better, faster, and more interactive web applications with
the help of XML, HTML, CSS, and Java Script.

AJAX is based on the following open standards:

Browser-based presentation using HTML and Cascading Style Sheets (CSS).

Data is stored in XML format and fetched from the server.

Behind-the-scenes data fetches using XMLHttpRequest objects in the
browser.

JavaScript to make everything happen.

从上面的解释中可以知道:ajax是一种技术方案,但并不是一种新技术。它依赖的是现有的CSS/HTML/Javascript,而其中最核心的依赖是浏览器提供的XMLHttpRequest对象,是这个对象使得浏览器可以发出HTTP请求与接收HTTP响应。

所以我用一句话来总结两者的关系:我们使用XMLHttpRequest对象来发送一个Ajax请求。

[ajax介绍]

想象一下,我们平常发朋友圈的时候,N张图片配上文字,嗖的一下就发出去了,不带任何拖泥带水的那种,体验感觉好爽~。

XMLHttpRequest的发展历程

XMLHttpRequest一开始只是微软浏览器提供的一个接口,后来各大浏览器纷纷效仿也提供了这个接口,再后来W3C对它进行了标准化,提出了XMLHttpRequest标准。XMLHttpRequest标准又分为Level
1和Level 2。

XMLHttpRequest Level 1主要存在以下缺点:

受同源策略的限制,不能发送跨域请求;

不能发送二进制文件(如图片、视频、音频等),只能发送纯文本数据;

在发送和获取数据的过程中,无法实时获取进度信息,只能判断是否完成;

那么Level 2对Level 1进行了改进,XMLHttpRequest Level 2中新增了以下功能:

可以发送跨域请求,在服务端允许的情况下

支持发送和接收二进制数据;

新增formData对象,支持发送表单数据;

发送和获取数据时,可以获取进度信息;

可以设置请求的超时时间;

  ajax是使用客户端脚本与web服务器交换数据的web应用开发方法。web页面不用打断交互流程进行重新加裁,就可以动态地更新。使用ajax,用户可以创建接近本地桌面应用的直接、高可用、更丰富、更动态的web用户界面。

但是我们停下来用技术手段思考一下,这可能吗?有些2G网络最多也就几十K网速,我们的图片随便一张都几M,即使压缩过也有几百K,怎么可能瞬间发出消息呢?

细说XMLHttpRequest如何使用

先来看一段使用XMLHttpRequest发送Ajax请求的简单示例代码。

function sendAjax() {

//构造表单数据

var formData = new FormData();

formData.append(‘username’, ‘johndoe’);

formData.append(‘id’, 123456);

//创建xhr对象

var xhr = new XMLHttpRequest();

//设置xhr请求的超时时间

xhr.timeout = 3000;

//设置响应返回的数据格式

xhr.responseType = “text”;

//创建一个 post 请求,采用异步

xhr.open(‘POST’, ‘/server’, true);

//注册相关事件回调处理函数

xhr.onload = function(e) {

if(this.status == 200||this.status == 304){

alert(this.responseText);

}

};

xhr.ontimeout = function(e) { … };

xhr.onerror = function(e) { … };

xhr.upload.onprogress = function(e) { … };

//发送数据

xhr.send(formData);

}

上面是一个使用xhr发送表单数据的示例,整个流程可以参考注释。

接下来我将站在使用者的角度,以问题的形式介绍xhr的基本使用。

我对每一个问题涉及到的知识点都会进行比较细致地介绍,有些知识点可能是你平时忽略关注的。

  异步网页特效和xml(ajax)不是什么新技术,而是使用几种现有技术——包括级联样式表(css教程)、网页特效、xhtml、xml和可扩展样式语言转换(xslt),开发外观及操作类似桌面软件的web应用软件。

现在想想,是不是有些诡异呢~

如何设置request header

在发送Ajax请求(实质是一个HTTP请求)时,我们可能需要设置一些请求头部信息,比如content-type、connection、cookie、accept-xxx等。xhr提供了setRequestHeader来允许我们修改请求
header。

oid setRequestHeader(DOMString header, DOMString value);

注意点

方法的第一个参数 header
大小写不敏感,即可以写成content-type,也可以写成Content-Type,甚至写成content-Type;

Content-Type的默认值与具体发送的数据类型有关,请参考本文【可以发送什么类型的数据】一节;

setRequestHeader必须在open()方法之后,send()方法之前调用,否则会抛错;

setRequestHeader可以调用多次,最终的值不会采用覆盖override的方式,而是采用追加append的方式。下面是一个示例代码:

var client = new XMLHttpRequest();

client.open(‘GET’, ‘demo.cgi’);

client.setRequestHeader(‘X-Test’, ‘one’);

client.setRequestHeader(‘X-Test’, ‘two’);

// 最终request header中”X-Test”为: one, two

client.send();

  [ajax执行原理]

其实吧,众多社交软件都是用了一种秒发机制。他并不是真的先去发送然后发送完毕再告诉你发送成功,而是直接告诉你发送成功,然后后台再偷偷去上传你发的东西,所以在网速不好的时候我们会经常发现一个现象,刚开始发出的朋友圈刚开始很正常,但是过了几分钟后,提示我们发送失败!这很很尴尬啊,明明刚开始你什么也没说啊,到了最关键的时候你告诉我不行了..

如何获取response header

xhr提供了2个用来获取响应头部的方法:getAllResponseHeaders和getResponseHeader。前者是获取
response 中的所有header 字段,后者只是获取某个指定 header
字段的值。另外,getResponseHeader(header)的header参数不区分大小写。

DOMString getAllResponseHeaders();

DOMString getResponseHeader(DOMString header);

这2个方法看起来简单,但却处处是坑儿。

你是否遇到过下面的坑儿?——反正我是遇到了。。。

使用getAllResponseHeaders()看到的所有response
header与实际在控制台Network中看到的response header不一样

使用getResponseHeader()获取某个header的值时,浏览器抛错Refused to get
unsafe header “XXX”

经过一番寻找最终在Stack
Overflow找到了答案。

原因1:W3C的 xhr
标准中做了限制,规定客户端无法获取
response 中的Set-Cookie、Set-Cookie2这2个字段,无论是同域还是跨域请求;

原因2:W3C 的 cors
标准对于跨域请求也做了限制,规定对于跨域请求,客户端允许获取的response
header字段只限于“simple response
header”和“Access-Control-Expose-Headers” (两个名词的解释见下方)。

“simple response header”包括的 header
字段有:Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma;

“Access-Control-Expose-Headers”:首先得注意是”Access-Control-Expose-Headers”进行跨域请求时响应头部中的一个字段,对于同域请求,响应头部是没有这个字段的。这个字段中列举的
header 字段就是服务器允许暴露给客户端访问的字段。

所以getAllResponseHeaders()只能拿到限制以外(即被视为safe)的header字段,而不是全部字段;而调用getResponseHeader(header)方法时,header参数必须是限制以外的header字段,否则调用就会报Refused
to get unsafe header的错误。

  一个ajax交互从一个称为xmlhttprequest的javascript对象开始。如同名字所暗示的,它允许一个客户端脚本来执行http请求,并且将会解析一个xml格式的服务器响应。ajax处理过程中的第一步是创建一个xmlhttprequest实例。使用http方法(get或post)来处理请求,并将目标url设置到xmlhttprequest对象上。

不要被高大上的技术吓到了,无非就是做了一些小技巧而已,真的很基础,但是也真的很实用。

如何指定xhr.response的数据类型

有些时候我们希望xhr.response返回的就是我们想要的数据类型。比如:响应返回的数据是纯JSON字符串,但我们期望最终通过xhr.response拿到的直接就是一个
js 对象,我们该怎么实现呢?

有2种方法可以实现,一个是level
1就提供的overrideMimeType()方法,另一个是level
2才提供的xhr.responseType属性。

xhr.overrideMimeType()

overrideMimeType是xhr level
1就有的方法,所以浏览器兼容性良好。这个方法的作用就是用来重写response的content-type,这样做有什么意义呢?比如:server
端给客户端返回了一份document或者是xml文档,我们希望最终通过xhr.response拿到的就是一个DOM对象,那么就可以用xhr.overrideMimeType(‘text/xml;
charset = utf-8’)来实现。

再举一个使用场景,我们都知道xhr level
1不支持直接传输blob二进制数据,那如果真要传输 blob
该怎么办呢?当时就是利用overrideMimeType方法来解决这个问题的。

下面是一个获取图片文件的代码示例:

var xhr = new XMLHttpRequest();

//向 server 端获取一张图片

xhr.open(‘GET’, ‘/path/to/image.png’, true);

// 这行是关键!

//将响应数据按照纯文本格式来解析,字符集替换为用户自己定义的字符集

xhr.overrideMimeType(‘text/plain; charset=x-user-defined’);

xhr.onreadystatechange = function(e) {

if (this.readyState == 4 && this.status == 200) {

//通过 responseText 来获取图片文件对应的二进制字符串

var binStr = this.responseText;

//然后自己再想方法将逐个字节还原为二进制数据

for (var i = 0, len = binStr.length; i < len; ++i) {

var c = binStr.charCodeAt(i);

//String.fromCharCode(c & 0xff);

var byte = c & 0xff;

}

}

};

xhr.send();

代码示例中xhr请求的是一张图片,通过将response的content-type改为’text/plain;
charset=x-user-defined’,使得xhr以纯文本格式来解析接收到的blob
数据,最终用户通过this.responseText拿到的就是图片文件对应的二进制字符串,最后再将其转换为
blob 数据。

xhr.responseType

responseType是xhr level
2新增的属性,用来指定xhr.response的数据类型,目前还存在些兼容性问题,可以参考本文的【XMLHttpRequest的兼容性】这一小节。那么responseType可以设置为哪些格式呢,我简单做了一个表,如下:

值                                                                    
            xhr.response数据类型                                        
                  说明

“”                                                                      
            String字符串                                                
                           默认值(在不设置responseType时)

“text”                                                                  
           String字符串

“document”                                                              
     Document对象                                                      
                  希望返回XML格式数据时使用

“json”                                                                  
           javascript对象                                              
                           存在兼容性问题,IE10/IE11不支持

“blob”                                                                  
           Blob对象

“arrayBuffer”                                                          
        ArrayBuffer对象

下面是同样是获取一张图片的代码示例,相比xhr.overrideMimeType,用xhr.response来实现简单得多。

var xhr = new XMLHttpRequest();

xhr.open(‘GET’, ‘/path/to/image.png’, true);

//可以将`xhr.responseType`设置为`”blob”`也可以设置为`”
arrayBuffer”`

//xhr.responseType = ‘arrayBuffer’;

xhr.responseType = ‘blob’;

xhr.onload = function(e) {

if (this.status == 200) {

var blob = this.response;

}

};

xhr.send();

小结

虽然在xhr level
2中,2者是共同存在的。但其实不难发现,xhr.responseType就是用来取代xhr.overrideMimeType()的,xhr.responseType功能强大的多,xhr.overrideMimeType()能做到的xhr.responseType都能做到。所以我们现在完全可以摒弃使用xhr.overrideMimeType()了。

  当你发送http请求,你不希望浏览器挂起并等待服务器的响应,取而代之的是,你希望通过页面继续响应用户的界面交互,并在服务器响应真正到达后处理它们。要完成它,你可以向xmlhttprequest注册一个回调函数,并异步地派发xmlhttprequest请求。控制权马上就被返回到浏览器,当服务器响应到达时,回调函数将会被调用。

在讨论技术前首先声明一些初始条件。

如何获取response数据

xhr提供了3个属性来获取请求返回的数据,分别是:xhr.response、xhr.responseText、xhr.responseXML

xhr.response

默认值:空字符串””

当请求完成时,此属性才有正确的值

请求未完成时,此属性的值可能是””或者null,具体与xhr.responseType有关:当responseType为””或”text”时,值为””;responseType为其他值时,值为null

xhr.responseText

默认值为空字符串””

只有当responseType为”text”、””时,xhr对象上才有此属性,此时才能调用xhr.responseText,否则抛错

只有当请求成功时,才能拿到正确值。以下2种情况下值都为空字符串””:请求未完成、请求失败

xhr.responseXML

默认值为null

只有当responseType为”text”、””、”document”时,xhr对象上才有此属性,此时才能调用xhr.responseXML,否则抛错

只有当请求成功且返回数据被正确解析时,才能拿到正确值。以下3种情况下值都为null:请求未完成、请求失败、请求成功但返回数据无法被正确解析时

 

1.对数据库表结构进行了一些特殊改造:
朋友圈内容表有一个特殊的字段status,
status值有两种情况,值可以为1或者2,值为1是朋友圈未发布。
值为2是朋友圈已发布。
(暂时看不懂为什么这样做的同学,可以先继续阅读,后面会讲解)

如何追踪ajax请求的当前状态

在发一个ajax请求后,如果想追踪请求当前处于哪种状态,该怎么做呢?

用xhr.readyState这个属性即可追踪到。这个属性是只读属性,总共有5种可能值,分别对应xhr不同的不同阶段。每次xhr.readyState的值发生变化时,都会触发xhr.onreadystatechange事件,我们可以在这个事件中进行相关状态判断。

xhr.onreadystatechange = function () {

switch(xhr.readyState){

case 1://OPENED

//do something

break;

case 2://HEADERS_RECEIVED

//do something

break;

case 3://LOADING

//do something

break;

case 4://DONE

//do something

break;

}

值           状态                                              
 描述

0            UNSENT(初始状态,未打开)        
 此时xhr对象被成功构造,open()方法还未被调用

1            OPENED(已打开,未发送)          
  open()方法已被成功调用,send()方法还未被调用。注意:只有xhr处于OPENED状态,才能调用xhr.setRequestHeader()和xhr.send(),否则会报错

2            HEADERS_RECEIVED(已获取响应头)    send()方法已经被调用,
响应头和响应状态已经返回

3            LOADING(正在下载响应体)             响应体(response entity
body)正在下载中,此状态下通过xhr.response可能已经有了响应数据

4            DONE(整个数据传输过程结束)      
 整个数据传输过程结束,不管本次请求是成功还是失败

 

2.此篇文章的秒发功能指的有图片的情况下使用,因为有图片的话,图片上传太慢,所以需要采用秒发机制,但是没有图片纯文字的话,就没太大的必要了,因为文本传输量很低,按照正常流程发送即可。

如何设置请求的超时时间

如果请求过了很久还没有成功,为了不会白白占用的网络资源,我们一般会主动终止请求。XMLHttpRequest提供了timeout属性来允许设置请求的超时时间。

xhr.timeout

单位:milliseconds 毫秒

默认值:0,即不设置超时

很多同学都知道:从请求开始算起,若超过timeout时间请求还没有结束(包括成功/失败),则会触发ontimeout事件,主动结束该请求。

【那么到底什么时候才算是请求开始?】

——xhr.onloadstart事件触发的时候,也就是你调用xhr.send()方法的时候。

因为xhr.open()只是创建了一个连接,但并没有真正开始数据的传输,而xhr.send()才是真正开始了数据的传输过程。只有调用了xhr.send(),才会触发xhr.onloadstart。

【那么什么时候才算是请求结束?】

——xhr.loadend事件触发的时候。

另外,还有2个需要注意的坑儿:

1.可以在send()之后再设置此xhr.timeout,但计时起始点仍为调用xhr.send()方法的时刻。

2.当xhr为一个sync同步请求时,xhr.timeout必须置为0,否则会抛错。原因可以参考本文的【如何发一个同步请求】一节。

 [ajax实际应用]

3.此篇文章的代码是基于PhalApi框架,语法都比较简单,有过ORM操作经验的同学应该都看得懂

如何发一个同步请求

xhr默认发的是异步请求,但也支持发同步请求(当然实际开发中应该尽量避免使用)。到底是异步还是同步请求,由xhr.open()传入的async参数决定。

open(method, url [, async = true [, username = null [, password =
null]]])

method: 请求的方式,如GET/POST/HEADER等,这个参数不区分大小写

url:
请求的地址,可以是相对地址如example.php,这个相对是相对于当前网页的url路径;也可以是绝对地址如

async: 默认值为true,即为异步请求,若async=false,则为同步请求

在我认真研读W3C 的 xhr
标准前,我总以为同步请求和异步请求只是阻塞和非阻塞的区别,其他什么事件触发、参数设置应该是一样的,事实证明我错了。

W3C 的 xhr标准中关于open()方法有这样一段说明:

Throws an “InvalidAccessError” exception if async is false, the
JavaScript global environment is a document environment, and either the
timeout attribute is not zero, the withCredentials attribute is true, or
the responseType attribute is not the empty string.

从上面一段说明可以知道,当xhr为同步请求时,有如下限制:

xhr.timeout必须为0

xhr.withCredentials必须为false

hr.responseType必须为””(注意置为”text”也不允许)

若上面任何一个限制不满足,都会抛错,而对于异步请求,则没有这些参数设置上的限制。

之前说过页面中应该尽量避免使用sync同步请求,为什么呢?

因为我们无法设置请求超时时间(xhr.timeout为0,即不限时)。在不限制超时的情况下,有可能同步请求一直处于pending状态,服务端迟迟不返回响应,这样整个页面就会一直阻塞,无法响应用户的其他交互。

另外,标准中并没有提及同步请求时事件触发的限制,但实际开发中我确实遇到过部分应该触发的事件并没有触发的现象。如在
chrome中,当xhr为同步请求时,在xhr.readyState由2变成3时,并不会触发onreadystatechange事件,xhr.upload.onprogress和xhr.onprogress事件也不会触发。

 

4.此篇文章主要讲解APP的秒发功能,WEB端不是特别需要这个功能,因为现代网络足以我们的PC一次性发送很多很多张图片

如何获取上传、下载的进度

在上传或者下载比较大的文件时,实时显示当前的上传、下载进度是很普遍的产品需求。

我们可以通过onprogress事件来实时显示进度,默认情况下这个事件每50ms触发一次。需要注意的是,上传过程和下载过程触发的是不同对象的onprogress事件:

上传触发的是xhr.upload对象的onprogress事件

下载触发的是xhr对象的onprogress事件

xhr.onprogress = updateProgress;

xhr.upload.onprogress = updateProgress;

function updateProgress(event) {

if (event.lengthComputable) {

var completedPercent = event.loaded / event.total;

}

}

  1. 初始化ajax

大方向讨论一下整个执行流程:

可以发送什么类型的数据

void send(data);

xhr.send(data)的参数data可以是以下几种类型:

ArrayBuffer

Blob

Document

DOMString

FormData

null

如果是
GET/HEAD请求,send()方法一般不传参或传null。不过即使你真传入了参数,参数也最终被忽略,xhr.send(data)中的data会被置为null.

xhr.send(data)中data参数的数据类型会影响请求头部content-type的默认值:

如果data是Document类型,同时也是HTML
Document类型,则content-type默认值为text/html;charset=UTF-8;否则为application/xml;charset=UTF-8;

如果data是DOMString类型,content-type默认值为text/plain;charset=UTF-8;

如果data是FormData类型,content-type默认值为multipart/form-data;
boundary=[xxx]

如果data是其他类型,则不会设置content-type的默认值

当然这些只是content-type的默认值,但如果用xhr.setRequestHeader()手动设置了中content-type的值,以上默认值就会被覆盖。

另外需要注意的是,若在断网状态下调用xhr.send(data)方法,则会抛错:Uncaught
NetworkError: Failed to execute ‘send’ on
‘XMLHttpRequest’。一旦程序抛出错误,如果不 catch
就无法继续执行后面的代码,所以调用xhr.send(data)方法时,应该用try-catch捕捉错误。

try{

xhr.send(data)

}catch(e) {

//doSomething…

};

xhr.withCredentials与CORS什么关系

我们都知道,在发同域请求时,浏览器会将cookie自动加在request
header中。但大家是否遇到过这样的场景:在发送跨域请求时,cookie并没有自动加在request
header中。

造成这个问题的原因是:在CORS标准中做了规定,默认情况下,浏览器在发送跨域请求时,不能发送任何认证信息(credentials)如”cookies”和”HTTP
authentication
schemes”。除非xhr.withCredentials为true(xhr对象有一个属性叫withCredentials,默认值为false)。

所以根本原因是cookies也是一种认证信息,在跨域请求中,client端必须手动设置xhr.withCredentials=true,且server端也必须允许request能携带认证信息(即response
header中包含Access-Control-Allow-Credentials:true),这样浏览器才会自动将cookie加在request
header中。

另外,要特别注意一点,一旦跨域request能够携带认证信息,server端一定不能将Access-Control-Allow-Origin设置为*,而必须设置为请求页面的域名。

  ajax实际上就是调用了xmlhttprequest对象,那么首先我们的就必须调用这个对象,我们构建一个初始化ajax的函数:
/**
* 初始化一个xmlhttp对象
*/

客户端调用发布API,服务端进行发布内容(publish.php),如果是有图片,那么客户端还要额外调用一个上传API(upload.php),在这个上传API(upload.php)还没完成工作的时候,客户端会直接告诉你发布成功(其实当前是没有上传完成,后面有一个进程在拼命帮你上传着呢),然后客户端会把你发的文字和图片暂时拼接好显示给你看(当前只有你能看到,你朋友圈其他人是看不到的),然后等待着上传API(upload.php)的结果/当然也可能上传超时,如果成功的话则顺利上传,失败的话则报发送失败,但是在等待结果的一分钟之内,他会先让你觉得你已经发送了,除非上传失败,才会在后来提醒你。

xhr相关事件

事件分类

xhr相关事件有很多,有时记起来还挺容易混乱。但当我了解了具体代码实现后,就容易理清楚了。下面是XMLHttpRequest的部分实现代码:

interface XMLHttpRequestEventTarget : EventTarget {

// event handlers

attribute EventHandler onloadstart;

attribute EventHandler onprogress;

attribute EventHandler onabort;

attribute EventHandler onerror;

attribute EventHandler onload;

attribute EventHandler ontimeout;

attribute EventHandler onloadend;

};

interface XMLHttpRequestUpload : XMLHttpRequestEventTarget {

};

interface XMLHttpRequest : XMLHttpRequestEventTarget {

// event handler

attribute EventHandler onreadystatechange;

readonly attribute XMLHttpRequestUpload upload;

};

从代码中我们可以看出

XMLHttpRequestEventTarget接口定义了7个事件:

onloadstart

onprogress

onabort

ontimeout

onerror

onload

onloadend

每一个XMLHttpRequest里面都有一个upload属性,而upload是一个XMLHttpRequestUpload对象

XMLHttpRequest和XMLHttpRequestUpload都继承了同一个XMLHttpRequestEventTarget接口,所以xhr和xhr.upload都有第一条列举的7个事件

onreadystatechange是XMLHttpRequest独有的事件

所以这么一看就很清晰了:

xhr一共有8个相关事件:7个XMLHttpRequestEventTarget事件+1个独有的onreadystatechange事件;而xhr.upload只有7个XMLHttpRequestEventTarget事件。

 代码如下

那么让我们在技术层面来分析一下这套机制吧:

事件触发条件

下面是我自己整理的一张xhr相关事件触发条件表,其中最需要注意的是onerror事件的触发条件。

事件                                   触发条件

onreadystatechange        
 每当xhr.readyState改变时触发;但xhr.readyState由非0值变为0时不触发。

onloadstart                      
  调用xhr.send()方法后立即触发,若xhr.send()未被调用则不会触发此事件。

onprogress                      
  xhr.upload.onprogress在上传阶段(即xhr.send()之后,xhr.readystate=2之前)触发,每50ms触发一次;xhr.onprogress在下载阶段(即xhr.readystate=3时)触发,每50ms触发一次。

onload                              
 当请求成功完成时触发,此时xhr.readystate=4

onloadend                        
 当请求结束(包括请求成功和请求失败)时触发

onabort                               当调用xhr.abort()后触发

ontimeout                          
 xhr.timeout不等于0,由请求开始即onloadstart开始算起,当到达xhr.timeout所设置时间请求还未结束即onloadend,则触发此事件。

onerror                                在请求过程中,若发生Network
error则会触发此事件(若发生Network
error时,上传还没有结束,则会先触发xhr.upload.onerror,再触发xhr.onerror;若发生Network
error时,上传已经结束,则只会触发xhr.onerror)。注意,只有发生了网络层级别的异常才会触发此事件,对于应用层级别的异常,如响应返回的xhr.statusCode是4xx时,并不属于Network
error,所以不会触发onerror事件,而是会触发onload事件。

function initajax()
{
 var ajax=false;
 try {
  ajax = new activexobject(“msxml2.xmlhttp”);
 } catch (e) {
  try {
   ajax = new activexobject(“microsoft.xmlhttp”);
  } catch (e) {
   ajax = false;
  }
 }
 if (!ajax && typeof xmlhttprequest!=’undefined’) {
  ajax = new xmlhttprequest();
 }
 return ajax;
}

当我们点上右上角的发送键后,有两个进程在同时启动,其中一个进程是帮你上传文本并且告诉你已经发送成功啦(publish.php),另外一个进程是偷偷去上传你发的图片(upload.php),具体代码如下:

事件触发顺序

当请求一切正常时,相关的事件触发顺序如下:

1.触发xhr.onreadystatechange(之后每次readyState变化时,都会触发一次)

2.触发xhr.onloadstart

//上传阶段开始:

3.触发xhr.upload.onloadstart

4.触发xhr.upload.onprogress

5.触发xhr.upload.onload

6.触发xhr.upload.onloadend

//上传结束,下载阶段开始:

7.触发xhr.onprogress

8.触发xhr.onload

9.触发xhr.onloadend

  

Publish.php

发生abort/timeout/error异常的处理

在请求的过程中,有可能发生abort/timeout/error这3种异常。那么一旦发生这些异常,xhr后续会进行哪些处理呢?后续处理如下:

1.一旦发生abort或timeout或error异常,先立即中止当前请求

2.将readystate置为4,并触发xhr.onreadystatechange事件

3.如果上传阶段还没有结束,则依次触发以下事件:

    xhr.upload.onprogress

    xhr.upload.[onabort或ontimeout或onerror]

    xhr.upload.onloadend

4.触发xhr.onprogress事件

5.触发xhr.[onabort或ontimeout或onerror]事件

6.触发xhr.onloadend事件

你也许会说,这个代码因为要调用xmlhttp组件,是不是只有ie浏览器能使,不是的经我试验,firefox也是能使用的。
那么我们在执行任何ajax操作之前,都必须先调用我们的initajax()函数来实例化一个ajax对象。

notorm-> friends ->insert; ?>

在哪个xhr事件中注册成功回调?

从上面介绍的事件中,可以知道若xhr请求成功,就会触发xhr.onreadystatechange和xhr.onload两个事件。
那么我们到底要将成功回调注册在哪个事件中呢?我倾向于xhr.onload事件,因为xhr.onreadystatechange是每次xhr.readyState变化时都会触发,而不是xhr.readyState=4时才触发。

xhr.onload = function () {

//如果请求成功

if(xhr.status == 200){

//do successCallback

}

}

上面的示例代码是很常见的写法:先判断http状态码是否是200,如果是,则认为请求是成功的,接着执行成功回调。这样的判断是有坑儿的,比如当返回的http状态码不是200,而是201时,请求虽然也是成功的,但并没有执行成功回调逻辑。所以更靠谱的判断方法应该是:当http状态码为2xx或304时才认为成功。

xhr.onload = function () {

//如果请求成功

if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){

//do successCallback

}

}

  2. 使用get方式

看出玄妙了吗?我们对入库的status字段进行了一次判断,分别会有1两种情况,那么在读取数据的时候我们应该怎么做呢(list.php)?

结语

最后给点扩展学习资料,如果你:

想真正搞懂XMLHttpRequest,最靠谱的方法还是看W3C的xhr
标准;

想结合代码学习如何用XMLHttpRequest发各种类型的数据,可以参考html5rocks上的这篇文章

想粗略的了解XMLHttpRequest的基本使用,可以参考MDN的XMLHttpRequest介绍;

想了解XMLHttpRequest的发展历程,可以参考阮老师的文章;

想了解Ajax的基本介绍,可以参考AJAX
Tutorial;

想了解跨域请求,则可以参考W3C的 cors
标准;

想了解http协议,则可以参考HTTP
Tutorial;

  现在我们第一步来执行一个get请求,加入我们需要获取
/show.php?id=1的数据,那么我们应该怎么做呢?

那么展示页面就是这样:

  假设有一个链接:<a
href=”/show.php?id=1″>新闻1</a>,我点该链接的时候,不想任何刷新就能够看到链接的内容,那么我们该怎么做呢?
//将链接改为:
<a href=”#” onclick=”getnews(1)”>新闻1</a>

Lists.php

//并且设置一个接收新闻的层,并且设置为不显示:
<div id=”show_news”></div>

notorm->pic->select->where("status > 1 OR (status = 1 && u_id = {$u_id})")->->fetchAll(); //code .. ?>

  同时构造相应的javascript函数:

此处的where条件是秒发机制的最关键的地方:

 代码如下

status大于1,(tipsstatus在有图片的情况下默认值为1)但是属于当前用户发布的内容,都可以读出来,这就有一个很奇妙的现象,就是无论如何,我们自己发的朋友圈,自己永远是可以读出来的,但是其他人就不一定了(因为如果有图片的话,还需要去调用另外一个进程上传图片,然后在那个进程将status改为2)

function getnews(newsid)
{
 //如果没有把参数newsid传进来
 if (typeof(newsid) == ‘undefined’)
 {
  return false;
 }
 //需要进行ajax的url地址
 var url = “/show.php?id=”+ newsid;

那么还有最后一个关键点,就是负责上传图片的那个进程(upload.php),这个是真实上传图片的逻辑,

 //获取新闻显示层的位置
 var show = document.getelementbyid(“show_news”);

有几张图片,这么upload.php就会被调用几次

 //实例化ajax对象
 var ajax = initajax();

每次上传成功后将图片行的字段status改成2

 //使用get方式进行请求
 ajax.open(“get”, url, true);

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图