`
D-tune
  • 浏览: 77572 次
  • 性别: Icon_minigender_1
  • 来自: 上海浦东
文章分类
社区版块
存档分类
最新评论

Javascript Callback的两种实现方案

阅读更多

 下文主要比较两种现有的Javascript callback实现机制,暂且定为简单版和完整版,简单版的实现只有几行代码,但是无法捕获错误响应,完整版的代码多一些确可以实现错误捕获,而且可以使用的更方便。

简单版

简单版的实现大致如下:

  1. var head = document.getElementsByTagName('head')[0];   
  2. var script = document.createElement('script');   
  3. /*onreadystatechange在IE下使用,onload在其他浏览器下使用*/  
  4. script.onload = script.onreadystatechange = function(){   
  5.     if( !script.readyState || (script.readyState && script.readyState == 'loaded'){   
  6.         //TODO   
  7.     }   
  8. }   
  9. head.appendChild(script);  
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
/*onreadystatechange在IE下使用,onload在其他浏览器下使用*/
script.onload = script.onreadystatechange = function(){
	if( !script.readyState || (script.readyState && script.readyState == 'loaded'){
		//TODO
	}
}
head.appendChild(script);

短短几行代码已经能满足基本的需求了,而且兼容性还很不错。不过无法提供出错处理,而且使用起来不方便。

 

完整版

具体的说明都注释在代码中,请看注释。

IE

  1. function loadIECallback(param){   
  2.     param = initParam(param);   
  3.     /*清除,防止内存泄露*/  
  4.     function clear(){   
  5.         frag = script = script.onreadystatechange = frag[param.name] = null;   
  6.     }   
  7.     var charset = param.charset || 'gb2312';   
  8.     /*创建一个DocumentFragment充当script元素的父容器,这样的好处是不需要将节点真正的加入到DOM树中*/  
  9.     var frag = document.createDocumentFragment(), script = document.createElement('script');   
  10.     script.charset = charset;   
  11.     /*将callback函数定义在DocumentFragment上,script返回后可以直接调用到该函数*/  
  12.     frag[param.name] = function () {   
  13.         /*调用用户传入的callback*/  
  14.         param.success && param.success.apply(null, arguments);   
  15.         /*使用完成后清除垃圾信息*/  
  16.         clear();   
  17.     };   
  18.     /*注册script的onreadystatechange事件  
  19.      *一般情况,IE6下script的readyState会经历loading、interactive和loaded,  
  20.      *IE7下没有interactive(表示加载的数据虽然未完成但已经可以使用)状态,  
  21.      *而在interactive(IE7下则是loading之后和loaded之前)状态后请求的callback数据其实已经到达并可用  
  22.      *也就是说如果正常请求到数据,则在loaded状态前定义在DocumentFragment上的Callback就会被执行。  
  23.      *由于加载成功后onreadystatechange事件会被清空,因此一旦readyState到达了loaded状态则表示加载错误。  
  24.      */  
  25.     script.onreadystatechange = function () {   
  26.         if (script.readyState == 'loaded') {   
  27.             param.error && param.error();   
  28.             clear();   
  29.         }   
  30.     };   
  31.     script.src = param.url;   
  32.     frag.appendChild(script);   
  33. }  
function loadIECallback(param){
	param = initParam(param);
	/*清除,防止内存泄露*/
	function clear(){
		frag = script = script.onreadystatechange = frag[param.name] = null;
	}
	var charset = param.charset || 'gb2312';
	/*创建一个DocumentFragment充当script元素的父容器,这样的好处是不需要将节点真正的加入到DOM树中*/
	var frag = document.createDocumentFragment(), script = document.createElement('script');
	script.charset = charset;
	/*将callback函数定义在DocumentFragment上,script返回后可以直接调用到该函数*/
	frag[param.name] = function () {
		/*调用用户传入的callback*/
		param.success && param.success.apply(null, arguments);
		/*使用完成后清除垃圾信息*/
		clear();
	};
	/*注册script的onreadystatechange事件
	 *一般情况,IE6下script的readyState会经历loading、interactive和loaded,
	 *IE7下没有interactive(表示加载的数据虽然未完成但已经可以使用)状态,
	 *而在interactive(IE7下则是loading之后和loaded之前)状态后请求的callback数据其实已经到达并可用
	 *也就是说如果正常请求到数据,则在loaded状态前定义在DocumentFragment上的Callback就会被执行。
	 *由于加载成功后onreadystatechange事件会被清空,因此一旦readyState到达了loaded状态则表示加载错误。
	 */
	script.onreadystatechange = function () {
		if (script.readyState == 'loaded') {
			param.error && param.error();
			clear();
		}
	};
	script.src = param.url;
	frag.appendChild(script);
}

其他浏览器

  1. function loadOther(param){   
  2.     param = initParam(param);   
  3.     /*清除,防止内存泄露*/  
  4.     function clear(){   
  5.         iframe.callback = iframe.errorcallback = null;   
  6.         iframe.src = 'about:blank', iframe.parentNode.removeChild(iframe), iframe = null;   
  7.     }   
  8.     var charset = param.charset || 'gb2312';   
  9.     /*采用iframe可以对错误进行响应*/  
  10.     var iframe = document.createElement('iframe');   
  11.     iframe.style.display = 'none';   
  12.   
  13.     /*在iframe的frameElement上定义callback*/  
  14.     iframe.callback = function () {   
  15.         param.success && param.success.apply(null, arguments);   
  16.         clear();   
  17.     };   
  18.     iframe.errorcallback = function () {   
  19.         param.error && param.error();   
  20.         clear();   
  21.     };   
  22.     try {   
  23.         document.body.appendChild(iframe);   
  24.         var doc = iframe.contentWindow.document;   
  25.         doc.open();   
  26.         doc.write(   
  27.             /*在iframe内容中定义真是的callback函数,以供正确请求到数据后进行响应,如果正确地请求到数据则会执行success回调并删除对错误的响应函数*/  
  28.             '<script type="text\/javascript">function ' + param.name + '() { window.frameElement.callback.apply(null, arguments); }</script>'  
  29.             /*写入script标签请求数据*/  
  30.             + '<script type="text\/javascript" src="' + param.url + '" charset="' + charset + '"></script>'  
  31.             /*如果没有请求道正确的数据,则会顺利执行完下面的脚步片段,对错误进行响应*/  
  32.             + '<script type="text\/javascript">window.setTimeout("try { window.frameElement.errorcallback(); } catch (exp) {}", 1)</script>'  
  33.         );   
  34.         doc.close();   
  35.     } catch (exp) {}   
  36. }  
function loadOther(param){
	param = initParam(param);
	/*清除,防止内存泄露*/
	function clear(){
		iframe.callback = iframe.errorcallback = null;
		iframe.src = 'about:blank', iframe.parentNode.removeChild(iframe), iframe = null;
	}
	var charset = param.charset || 'gb2312';
	/*采用iframe可以对错误进行响应*/
	var iframe = document.createElement('iframe');
	iframe.style.display = 'none';

	/*在iframe的frameElement上定义callback*/
	iframe.callback = function () {
		param.success && param.success.apply(null, arguments);
		clear();
	};
	iframe.errorcallback = function () {
		param.error && param.error();
		clear();
	};
	try {
		document.body.appendChild(iframe);
		var doc = iframe.contentWindow.document;
		doc.open();
		doc.write(
			/*在iframe内容中定义真是的callback函数,以供正确请求到数据后进行响应,如果正确地请求到数据则会执行success回调并删除对错误的响应函数*/
			'<script type="text\/javascript">function ' + param.name + '() { window.frameElement.callback.apply(null, arguments); }</script>'
			/*写入script标签请求数据*/
			+ '<script type="text\/javascript" src="' + param.url + '" charset="' + charset + '"></script>'
			/*如果没有请求道正确的数据,则会顺利执行完下面的脚步片段,对错误进行响应*/
			+ '<script type="text\/javascript">window.setTimeout("try { window.frameElement.errorcallback(); } catch (exp) {}", 1)</script>'
		);
		doc.close();
	} catch (exp) {}
}

使用实例

最简洁的方式如下,这里省略了name和key,那么实际上在实现时会自动生成一个带时间戳的callback name,并且默认key为callback,因此最后请求的URL应该为:
http://youa.baidu.com/suggest/se/s?cmd=suggest&type=kwd&max_count=10&keyword=xxx&callback=_callback_func_123123112。

  1. Connection.loadCallback({   
  2.     url: 'http://youa.baidu.com/suggest/se/s?cmd=suggest&type=kwd&max_count=10&keyword=' + keyword,   
  3.     success: successHandle1,   
  4.     error: errorHandle   
  5. });  
Connection.loadCallback({
	url: 'http://youa.baidu.com/suggest/se/s?cmd=suggest&type=kwd&max_count=10&keyword=' + keyword,
	success: successHandle1,
	error: errorHandle
});

此外,如果返回的callback函数名是写死的,则需要指定该callback的名字,即参数中的name,使用方式如下:

  1. Connection.loadCallback({   
  2.     name: 'JSCallback',   
  3.     url: 'http://youa.baidu.com/suggest/se/s?cmd=suggest&type=kwd&max_count=10&keyword=' + keyword,   
  4.     success: successHandle1,   
  5.     error: errorHandle   
  6. });  
Connection.loadCallback({
	name: 'JSCallback',
	url: 'http://youa.baidu.com/suggest/se/s?cmd=suggest&type=kwd&max_count=10&keyword=' + keyword,
	success: successHandle1,
	error: errorHandle
});

此外,如果返回的callback函数名是通过传参来指定的,则可以通过name和key来配置,其中name是callback函数名,key则是传参过程中的参数名,使用方式如下:

  1. Connection.loadCallback({   
  2.     name: 'JSCallback',   
  3.     key: 'cb',   
  4.     url: 'http://youa.baidu.com/suggest/se/s?cmd=suggest&type=kwd&max_count=10&keyword=' + keyword,   
  5.     success: successHandle1,   
  6.     error: errorHandle   
  7. });  
Connection.loadCallback({
	name: 'JSCallback',
	key: 'cb',
	url: 'http://youa.baidu.com/suggest/se/s?cmd=suggest&type=kwd&max_count=10&keyword=' + keyword,
	success: successHandle1,
	error: errorHandle
});

上例中,发出的请求URL应该类似:http://youa.baidu.com/suggest/se/s?cmd=suggest&type=kwd&max_count=10&keyword=&cb=JSCallback

具体的例子请看这里

总结一下

  1. 在IE的实现方案中,将DocumentFragment充当script元素的父容器,并且将用户定义的callback函数定义在DocumentFragment上,这样的好处是不需要将节点真正的加入到DOM树中即可完成callback功能。
  2. 在IE的实现方案中,利用script在readyState为loaded之前就已经可以做交互的原理实现了出错处理,如果请求正确则在readyState转变为loaded之前就应经完成响应,并删除script,所以如果readyState转变到loaded则视为error。
  3. 在IE之外的实现方案中,利用iframe实现了出错处理,脚本是顺序执行的,正确的响应执行后便销毁iframe,这样便没有机会执行后面的出错响应,只有当请求出错时方会执行。
分享到:
评论

相关推荐

    ASP.NET2.0实现无刷新客户端回调的Callback机制

    在ASP.NET 2.0中,有两种触发客户端回调的方式:同步和异步。同步回调(PostBack)会导致整个页面生命周期执行,而异步回调(AsyncPostBack)只更新指定的UpdatePanel。异步回调是无刷新更新的主要实现方式,通常...

    Javascript跨域访问解决方案

    这是一种适用于两个不同源的窗口之间的通信方式,通过`postMessage`方法发送数据,另一窗口监听`message`事件接收数据。这种方法常用于嵌入式框架(如iframe)的跨域通信。 7. document.domain 对于同一二级域名下...

    JavaScript中Promises/A+规范的实现

    JavaScript中的Promise是异步编程的一种解决方案,它解决了回调地狱(Callback Hell)问题,使得复杂的异步操作更加清晰、可读性更强。Promise/A+规范是Promise实现的一个标准,旨在提供一致性和可移植性。该规范...

    javascript多浏览器语音播报,文字转语音

    在JavaScript中实现文字转语音主要依赖于Web Speech API,这是一个由W3C制定的标准,包括两个关键部分:SpeechSynthesis(语音合成)和SpeechRecognition(语音识别)。我们的场景主要涉及SpeechSynthesis,它允许...

    JavaScript解决跨域滚动条

    为了解决这个问题,我们可以利用JavaScript中的两种主要方法来实现跨域通信:JSONP(JSON with Padding)和CORS(Cross-Origin Resource Sharing)。这两种方法都允许不同源之间的数据交换,但适用于不同的场景。 1...

    javascript跨域访问的方法.pdf

    以下是两种主要的JavaScript跨域技术:基于iframe的跨域和JSONP(JSON with Padding)。 1. 基于iframe实现跨域: 当两个页面的基础域相同,如aa.xx.com和bb.xx.com,可以通过设置`document.domain`来实现跨子域的...

    javaScript跨域通信

    由于浏览器的同源策略,一个域名下的JavaScript无法直接访问或操作不同域名下的资源,这就需要我们采用特定的方法来实现跨域通信。在这个话题中,我们将深入探讨如何克服这一限制,并通过示例来解释具体的操作步骤。...

    JavaScript语言精粹5

    - **数据类型**: JavaScript中有两种主要的数据类型——原始类型(如Number、String、Boolean等)和对象类型(包括Array、Object等)。需要注意的是,在JavaScript中null和undefined也是两种特殊的类型。 **1.2 ...

    受微信webview启发的Android Java和javascript桥.zip

    微信Webview的实现提供了一种高效且稳定的解决方案,即Java和JavaScript之间的桥接机制。本篇文章将深入探讨这一机制,并基于这个思路,介绍如何在Android项目中构建类似的Java和JavaScript桥。 首先,理解Webview...

    前端基础-Ajax跨域问题的解决方案.docx

    Ajax 跨域问题主要涉及到 JSONP 和 CORS 两种解决方案。 10.1 JSONP(JSON with Padding) JSONP 是一种非正式的协议,用于解决 JavaScript 的跨域数据交互问题。它的核心思想是利用 `&lt;script&gt;` 标签可以跨域加载...

    javascript跨域原因以及解决方案分享

    5. Window.postMessage:这是一种允许来自不同源的脚本采用异步方式进行有限通信的方式,通过它,可以实现两个具有不同源的页面之间的跨域通信。 每种方法都有其适用场景和局限性,例如JSONP不支持POST请求,CORS...

    JavaScript多线程编程简介.txt

    4. **改进版的getArticleWithCache()和backgroundLoad()函数**:为了支持异步操作,这两个函数进行了改造,增加了回调函数(callback)的支持,使得可以在数据加载完成后执行相应的操作。 ```javascript function ...

    跨域请求解决方案源代码(JSONP,CORS)

    在Web开发中,由于浏览器的同源策略限制,JavaScript无法直接访问不同源的资源,这在进行前后端分离或者API调用时会遇到问题。"跨域请求解决方案源代码...理解和掌握这两种技术对于进行前后端分离的Web开发至关重要。

    JavaScript 跨域通信方法

    本文重点介绍了两种类型的跨域问题及其解决方案。对于不同源的接口请求,可以采用JSONP或XMLHttpRequest Level 2等技术;而对于父子页面间的通信,则可以通过代理页面或`postMessage` API来实现。开发者可以根据实际...

    请求跨域的解决方案.docx

    总的来说,请求跨域的解决方案主要有JSONP和CORS两种方式。JSONP适用于简单的GET请求,而CORS则提供了一套完整的跨域安全策略,可以处理各种复杂的跨域场景。在实际开发中,根据应用场景选择合适的解决方案,确保...

    WtfJS一个有趣和棘手的JavaScript示例列表

    JavaScript有两种作用域:全局作用域和函数作用域,但没有块级作用域。这意味着,`if`语句、`for`循环等内部声明的变量在外部仍然可访问,如`var`声明的变量`i`在`for`循环外仍然存在,这在其他一些语言中是不允许的...

    Promise的简单实现

    在JavaScript的世界里,Promise是一种处理异步操作的重要工具,它为复杂的回调地狱提供了一种更加清晰、可读性更强的解决方案。Promise有三种状态:pending(等待中)、fulfilled(已成功)和rejected(已失败)。当...

    Nodejs回调加超时限制两种实现方法

    接下来将分别详细解释这两种方法的实现原理和代码实现。 ### 方法一:使用`async`模块的`parallel`方法 `async`是一个流行的Node.js库,它提供了一系列辅助异步操作的函数。其中`parallel`方法可以并行地执行多个...

    跨域解决方案

    今天,我们主要讨论 jsonp 跨域和 document.domain + iframe 跨域这两种解决方案。 Jsonp 跨域 jsonp 是一种常见的解决方案,通常用于减轻 web 服务器的负载。我们可以通过动态创建 script 标签,请求一个带参数的...

Global site tag (gtag.js) - Google Analytics