Last active
May 19, 2020 03:27
-
-
Save yyx990803/5096628 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title></title> | |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> | |
<style> | |
body { | |
font-family: 'Helvetica Neue', Arial, 'Microsoft Yahei', sans-serif; | |
padding: 0 30px; | |
color: #333; | |
} | |
a { | |
color: #F33; | |
} | |
p { | |
width: 724px; | |
} | |
.column { | |
display: inline-block; | |
width: 360px; | |
border: 1px solid #ccc; | |
vertical-align: top; | |
padding: 20px 30px; | |
box-sizing: border-box; | |
-moz-box-sizing: border-box; | |
} | |
.received, .displayed { | |
height: 10px; | |
position: relative; | |
margin-bottom: 10px; | |
} | |
label { | |
width: 60px; | |
font-size: 12px; | |
} | |
.grid { | |
display: inline-block; | |
box-sizing: border-box; | |
-moz-box-sizing: border-box; | |
height: 10px; | |
border: 1px solid #ccc; | |
} | |
.grid.red { | |
border-color: #fff; | |
background-color: #f33; | |
} | |
.grid.green { | |
border-color: #fff; | |
background-color: #6c6; | |
} | |
#results-1, #results-2 { | |
margin-top: 20px; | |
} | |
#progress-1, #progress-2 { | |
margin-top: 10px; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>前端异步API调用</h1> | |
<p> | |
问题的起因:参见耗子的<a href="http://weibo.com/1401880315/zlWwkwIvU" target="_blank">原贴1</a> & | |
<a href="http://weibo.com/1401880315/zm4Yv2ZPO" target="_blank">原贴2</a> | |
</p> | |
<p style="color: #69F">注:耗子的测试页面一开始给出的xss_rpc_call有问题,这里做了修正,等同于现在测试页面里的xss_rpc_call2</p> | |
<p>耗子的<a href="http://coolshell.cn/t.html" target="_blank">测试页面</a>上现有的5个方法,原本那个错的就不说了,闭包方法在返回延时有波动的情况下无法保证显示顺序;回调和自制Async每一次请求都要等待一次,效率过低;最后的@雪飞霜舞的解决方案和我最早贴出的<a href="https://gist.github.com/yyx990803/5084020" target="_blank">gist</a> | |
思路是一样的:在修正xss_rpc_call的前提下,把返回的值按照请求发出顺序储存在一个数组里,等待所有请求都完成,一次性打印结果。我们把这个方法姑且称作 Wait All。</p> | |
<p>但这样的解决方案有个问题,那就是在api端返回延迟波动很大的情况下,用户会要等待很久才能看到结果。理想的方案应该既保证数据显示的顺序,又保证以最快的速度显示能显示的所有数据。这里提供了此方案的实现,称之为ASAP (as soon as possible)。</p> | |
<p>具体算法见<a href="https://gist.github.com/yyx990803/5096628" target="_blank">本页源码</a>。可以用下面的按钮比较两个方案的显示输出。</p> | |
<p><input type="checkbox" checked="true" id="delay"> 模拟随机返回延迟 (0 ~ 1000ms,会打乱返回的顺序)</p> | |
<div style="min-width:724px"> | |
<div class="column"> | |
<button onclick="async_asap(20)">ASAP</button> | |
<div id="progress-1"> | |
<label>Received</label> | |
<div class="received"> | |
</div> | |
<label>Displayed</label> | |
<div class="displayed"> | |
</div> | |
</div> | |
<div id="results-1"></div> | |
</div> | |
<div class="column"> | |
<button onclick="wait_all(20)">Wait All</button> | |
<div id="progress-2"> | |
<label>Received</label> | |
<div class="received"> | |
</div> | |
<label>Displayed</label> | |
<div class="displayed"> | |
</div> | |
</div> | |
<div id="results-2"></div> | |
</div> | |
</div> | |
<script> | |
// coolshell api | |
// 对xss_rpc_call作了少许修改 =================================================== | |
function xss_ajax(url, callback) { | |
var script_id = null; | |
var script = document.createElement('script'); | |
script.setAttribute('type', 'text/javascript'); | |
script.setAttribute('src', url); | |
script.setAttribute('id', 'coolshell_script_id'); | |
script.onload = script.onreadystatechange = function(){ | |
if((!this.readyState || this.readyState === "loaded" || this.readyState === "complete")){ | |
callback && callback(); | |
} | |
}; | |
script_id = document.getElementById('coolshell_script_id'); | |
if(script_id){ | |
document.getElementsByTagName('head')[0].removeChild(script_id); | |
} | |
// Insert <script> into DOM | |
document.getElementsByTagName('head')[0].appendChild(script); | |
} | |
var xss_callbacks = {} // 容纳callbacks | |
function xss_rpc_call(n, callback) { | |
// 设置对应的jsonp callback | |
var callbackName = encodeURI('xss_callbacks[' + n + ']'); | |
var url = "http://coolshell.cn/t.php?n="+n+"&callback="+callbackName; | |
xss_ajax(url); | |
// 现在每一次请求对应一个不同的callback | |
xss_callbacks[n] = function(result){ | |
xss_callbacks[n] = null; // clean up | |
// 模拟随机延时 | |
var checked = document.getElementById('delay').checked; | |
setTimeout(function () { | |
callback(result); | |
}, checked ? Math.random() * 1000 : 0); | |
} | |
} | |
function display_result(id, text) | |
{ | |
var div = document.getElementById(id); | |
var new_div = document.createElement("div"); | |
new_div.appendChild(document.createTextNode(text)); | |
div.appendChild(new_div); | |
} | |
// ASAP方法 ==================================================== | |
function async_asap (x) { | |
document.getElementById('results-1').innerHTML = '' | |
initProgress(1, x) | |
var responses = [], | |
displayed = [] | |
for (var i = 0; i < x; i++) { | |
(function (i) { | |
var now = Date.now() | |
xss_rpc_call(i, function (res) { | |
responses[i] = { | |
res: res, | |
sent: now | |
} | |
updateProgress('receive', 1, i) | |
// only display if | |
// - this is the first request | |
// or | |
// - the previous request has been displayed | |
if (i == 0 || displayed[i-1]) { | |
display(i) | |
} | |
}) | |
})(i) | |
} | |
function display (i) { | |
display_result('results-1', i + ' --- ' | |
+ responses[i].res + ' --- ' | |
+ (Date.now() - responses[i].sent) + 'ms') | |
displayed[i] = true | |
updateProgress('display', 1, i) | |
if (i === x - 1) { | |
// all done. | |
} else if (responses[i+1]) { | |
// recursively display the next request if it has been received. | |
display(i+1) | |
} | |
} | |
} | |
// Wait All 方法 =================================================== | |
function wait_all (total) { | |
document.getElementById('results-2').innerHTML = '' | |
initProgress(2, total) | |
var temp = new Array(total), | |
n = total; | |
for (var i = 0; i < total; i++) { | |
var cb = (function(j){ | |
var start = Date.now(); | |
return function(result){ | |
n--; | |
temp[j] = result; | |
updateProgress('receive', 2, j) | |
if(n == 0){ | |
while(temp.length){ | |
updateProgress('display', 2, n) | |
display_result ("results-2", (n++) + ' --- ' | |
+ temp.shift() + ' --- ' | |
+ (Date.now() - start) + 'ms'); | |
} | |
} | |
}; | |
})(i); | |
xss_rpc_call(i, cb); | |
} | |
} | |
// 以下是用来可视化接收和显示的进度 ============================================== | |
var progressBars = { | |
receive1 : document.querySelector('#progress-1 .received'), | |
display1 : document.querySelector('#progress-1 .displayed'), | |
receive2 : document.querySelector('#progress-2 .received'), | |
display2 : document.querySelector('#progress-2 .displayed') | |
} | |
var colors = { | |
receive: 'red', | |
display: 'green' | |
} | |
function initProgress (id, total) { | |
var grids = getGridsHTML(total) | |
progressBars['receive'+id].innerHTML = grids | |
progressBars['display'+id].innerHTML = grids | |
} | |
function getGridsHTML (total) { | |
var html = '' | |
for (i = 0; i < total; i++) { | |
html += '<div class="grid" data-index="' + i + '" style="width:' + Math.floor(100/total) + '%"></div>' | |
} | |
return html | |
} | |
function updateProgress (event, id, index) { | |
var grid = progressBars[event+id].querySelector('.grid[data-index="' + index + '"]') | |
grid.classList.add(colors[event]) | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment