Skip to content

Instantly share code, notes, and snippets.

@yyx990803
Last active May 19, 2020 03:27
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save yyx990803/5096628 to your computer and use it in GitHub Desktop.
Save yyx990803/5096628 to your computer and use it in GitHub Desktop.
<!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> &amp;
<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