console.log('1'); setTimeout(function(){ console.log('first time'); }, 10); console.log('2'); var flag = true; setTimeout(function(){ flag = false; console.log('second time'); }, 0); var t_start = Number(new Date()); //延迟一秒才执行 while ( t_start + 1000 > Number(new Date()) ) { console.log(flag); } console.log('last ' + flag);
输出:


var image = new Image(); console.log('1.image async start'); image.src = 'http://www.baidu.com/img/bdlogo.gif'; image.onload = function(){ console.log('2.image loading'); }; console.log('3.load over'); //image.src = 'http://www.baidu.com/img/1.png'; image.onerror = function(){ console.log('4.image error'); }; var t_start = Number(new Date()); //延迟一秒才执行 while ( t_start + 1000 > Number(new Date()) ) { console.log('while true'); } console.log('5.error over');
输出:

script = doc.createElement("script");
script.setAttribute("type", "text/javascript"); script.setAttribute("src", scriptUrl); window.document.body.appendChild(script);
window[callback_name] = function(a){ //do something };
动态script加载的js代码,因为这个过程是异步的,所以通过回调函数来执行也就变成了异步。
这种异步方式,依赖于ajax的异步,如果将它改成同步,那么他就是同步的。
2、XHR Injection
用 XHR 下载,在页面中动态创建一个 script 元素,并将 responseText 作为其 text 。
这种方式和1在本质上是一致的。
3、Script in Iframe
把脚本放在 HTML 中,使用 ifame 来下载它,iframe的内容加载是异步的,所以js代码的加载也是异步的。
4、Script DOM Element
动态创建一个 script 元素,把 src 指向脚本URL。
script代码是动态加载的。
5、Script Defer
给 script 标添加 defer 属性。高版本浏览器才支持。
6、document.write Script Tag
利用 document.write 把 <script src=""> 添加到 HTML 中。
document.write(unescape("%3Cscript src='url' type='text/javascript'%3E%3C/script%3E"));
有人说,上面讲了这么多,你到底想要说明什么,是不是深井冰啊。
正如,没有买卖,就没有杀害。
所以下面进入正题...
正题
需求来源是使用html2canvas(开源插件访问这里)这个插件,实现对网页的截图。
html2canvas的原理就是根据指定的父节点dom,在canvas里面重绘所有的dom子节点,
然后将canvas另存为base64的png图片,最后实现截图。
普通的dom节点,都可以直接画出来,但对于跨域的图片需要用代理来动态加载图片,其实也是解决跨域的一种方式。
这个插件当然是不错的,费时的它当然也是采取异步的方式来做的。
但实际上,我们平台需求和实现的恶心程度,直接导致如果使用异步的方式,会影响最后的呈现效果,
所以,我们的终极boss就是需要将这个lib改为同步的方式,然后就有了上面一大段技术调研。
如上,知道了异步编程的N种方式,那么要将异步改为同步,就很简单啦,无非就是将延时改为及时嘛。
上html2canvas的源码:
1、首先映入眼帘的是img.onload 和 img.onerror
在保证跨域截图没有404错误图片的情况下,我们可以将img.onerror直接去掉,消除这个异步影响。
对于img.onload,直接将其方法体拿出来,将异步变为同步,最后setImageLoadHandlers方法变为:
function setImageLoadHandlers(img, imageObj) { //img.onload = function() { if ( imageObj.timer !== undefined ) { // CORS succeeded window.clearTimeout( imageObj.timer ); } images.numLoaded++; imageObj.succeeded = true; img.onerror = img.onload = null; start(); //}; }
这一步大功告成。
但在配置proxy的前提下,执行起来,依然是异步的,一步步调试......了很久很久...
2、proxyGetImage方法
// TODO modify proxy to serve images with CORS enabled, where available function proxyGetImage(url, img, imageObj){ var callback_name, scriptUrl = options.proxy, script; link.href = url; url = link.href; // work around for pages with base href="" set - WARNING: this may change the url callback_name = 'html2canvas_' + (count++); imageObj.callbackname = callback_name; if (scriptUrl.indexOf("?") > -1) { scriptUrl += "&"; } else { scriptUrl += "?"; } scriptUrl += 'url=' + encodeURIComponent(url) + '&callback=' + callback_name; script = doc.createElement("script"); window[callback_name] = function(a){ if (a.substring(0,6) === "error:"){ imageObj.succeeded = false; images.numLoaded++; images.numFailed++; start(); } else { setImageLoadHandlers(img, imageObj); img.src = a; } window[callback_name] = undefined; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9) try { delete window[callback_name]; // for all browser that support this } catch(ex) {} script.parentNode.removeChild(script); script = null; delete imageObj.script; delete imageObj.callbackname; }; script.setAttribute("type", "text/javascript"); script.setAttribute("src", scriptUrl); imageObj.script = script; window.document.body.appendChild(script); }
在这个方法的最后,发现了动态创建script的踪迹,它通过引入scriptUrl得到的jsonp的可执行js代码,
找到window[callback_name]注册的回调方法,来异步的获取跨域图片,这个就是异步执行的另一个源头。
找到了源头,就找到了解决方法。
那就改呗。
为达到一样的效果,使用前述“js代码异步加载的几种方式”的第一种方法【1、XHR Eval】来加载需要回调执行的js代码,并保证它是同步的,
这时候,你要不要再回去看看这个方法?
不用!ok,修改后的代码如下:
$.ajax({ url: scriptUrl, type: 'GET', success: function(res){ eval('(' + res + ')'); }, async: false//同步 });
这样,万事大吉,马上能同步分享。
-------------------------------------------华丽丽的分割线-----------------------------------------------
如需查看效果,请前往:【百度众测-我的成长时间轴】,预计2014年2月20晚上8点正式上线,如不能访问,请在跳转的页面稍作停留,耐心等待。
如需转载,请注明来自:http://www.cnblogs.com/quenteenfix/
原文地址:http://www.cnblogs.com/quenteenfix/p/3556269.html
3ks...