我正在使用async.js library by Caolan McMahon和jQueryUI progress bar向用户提供反馈,而多个异步调用会收集数据并填充复杂图形的元素。
我的问题是:异步方法需要共享数据范围的最佳方法是什么?
这是我正在做的简化示例。我已经使用全局变量工作,但他们打扰了我一点,让jsLint抱怨。在文档就绪功能中传递参数或作用域会破坏它。
updateA()
等的同行。人。在我的真实代码中有数百行并包含XHR调用。
// global variables. Bad?
var steps = 3;
var ticked = 0;
var otherCounter = 0;
$(function() {
$('#progressbar').progressbar({
value: 0
});
async.parallel([
function(onDoneCallback) {
updateA(onDoneCallback);},
function(onDoneCallback) {
updateB(onDoneCallback);},
function(onDoneCallback) {
updateC(onDoneCallback);}
], function(err, results) { // final callback when they're all done
tickProgress('All done after ' + ticked + ' ticks.', true);
});
});
function tickProgress(message) {
var curvalue = $('#progressbar').progressbar('option', 'value');
var done = false;
if (arguments.length > 1) {
done = arguments[1];
}
$('#progress_text').html(message);
if (done) {
$('#progressbar').progressbar('option', 'value', 100);
}
else {
$('#progressbar').progressbar('option', 'value', curvalue + 100 / steps);
}
ticked++; // global OK here?
}
function updateA(onDoneCallback) {
setTimeout(function() {
$('#a').html('A is foo. otherCounter ' + otherCounter);
tickProgress('updated A at otherCounter ' + otherCounter);
otherCounter++;
onDoneCallback(null, 'A done');
}, 1000);
}
function updateB(onDoneCallback) {
setTimeout(function() {
$('#b').html('B is bottle. otherCounter ' + otherCounter);
tickProgress('updated B at otherCounter ' + otherCounter);
otherCounter++;
onDoneCallback(null, 'B is OK');
}, 100);
}
function updateC(onDoneCallback) {
setTimeout(function() {
$('#c').html('C is cauliflower. otherCounter ' + otherCounter);
tickProgress('updated C at otherCounter ' + otherCounter);
otherCounter++;
onDoneCallback(null, 'C done');
}, 2000);
}
<p id="progress_text" style="background:yellow">Loading...</p>
<div id="progressbar"></div>
<hr />
<h2>a</h2>
<p id="a">Looking up a...</p>
<h2>b</h2>
<p id="b">Looking up b...</p>
<h2>c</h2>
<p id="c">Looking up c...</p>
如果你想在那里敲打它,我已经the sample code at JSFiddle了。
答案 0 :(得分:5)
总的来说,创建自己的关闭 Function-Context'ed
“区域”总是一个好主意。您可以通过在应用程序周围包装一个自调用匿名函数来实现。这可能看起来像
(function(window, document, $) {
// all your app logic goes into here
var steps = 3;
var ticked = 0;
var otherCounter = 0;
// ...
}(this, this.document, jQuery))
这样,你永远不会破坏全局命名空间。当然,你有时需要有一个全局对象,但除非绝对必要,否则你应该尽量避免这种情况。
答案 1 :(得分:3)
您可以将全局变量放入“状态”对象并将其传递给所有回调函数。 e.g。
$(function() {
var progressState = {
steps: 3,
ticked: 0,
otherCounter: 0
};
$('#progressbar').progressbar({value: 0});
async.parallel(
[
function(onDoneCallback) {updateA(onDoneCallback, progressState);},
function(onDoneCallback) {updateB(onDoneCallback, progressState);},
function(onDoneCallback) {updateC(onDoneCallback, progressState);}
],
function(err, results) { // final callback when they're all done
tickProgress('All done after ' + progressState.ticked + ' ticks.', true, progressState);
}
);
});
然后,您将更改updateA,updateB,updateC和tickProgress以使用提供的状态对象,而不是全局变量。
答案 2 :(得分:2)
david让我在JS聊天室中找到了这种方法the other day。 See it work at jsfiddle.
它使用全局对象,但我更喜欢对象内方法的逻辑封装,而不是jAndy和kirilloid的建议。
var Updater = (function() {
var steps = 3;
var ticked = 0;
var otherCounter = 0;
function a(onDoneCallback) {
setTimeout(function() {
$('#a').html('A is foo. otherCounter ' + otherCounter);
tickProgress('updated A at otherCounter ' + otherCounter);
otherCounter++;
onDoneCallback(null, 'A done');
}, 1000);
}
function b(onDoneCallback) {
setTimeout(function() {
$('#b').html('B is bottle. otherCounter ' + otherCounter);
tickProgress('updated B at otherCounter ' + otherCounter);
otherCounter++;
onDoneCallback(null, 'B is OK');
}, 100);
}
function c(onDoneCallback) {
setTimeout(function() {
$('#c').html('C is cauliflower. otherCounter ' + otherCounter);
tickProgress('updated C at otherCounter ' + otherCounter);
otherCounter++;
onDoneCallback(null, 'C done');
}, 2000);
}
function tickProgress(message) {
var curvalue = $('#progressbar').progressbar('option', 'value');
var done = false;
if (arguments.length > 1) {
done = arguments[1];
}
$('#progress_text').html(message);
if (done) {
$('#progressbar').progressbar('option', 'value', 100);
}
else {
$('#progressbar').progressbar('option', 'value', curvalue + 100 / Updater.getSteps());
}
Updater.tick(); // global OK here?
}
return {
a: a,
b: b,
c: c,
tickProgress: tickProgress,
tick: function() {
ticked++;
},
getTicks: function() {
return ticked;
},
getSteps: function() {
return steps;
}
};
}());
$(function() {
$('#progressbar').progressbar({
value: 0
});
async.parallel([
function(onDoneCallback) {
Updater.a(onDoneCallback);},
function(onDoneCallback) {
Updater.b(onDoneCallback);},
function(onDoneCallback) {
Updater.c(onDoneCallback);}
], function(err, results) { // final callback when they're all done
Updater.tickProgress('All done after ' + Updater.getTicks() + ' ticks.', true);
});
});