Asynchronously delay JS until a condition is met
I have a class, ChatRoom
, that can only render after it receives a long-running HTTP request (it could take 1 second or 30 seco开发者_如何学Gonds). So I need to delay rendering until ChatRoom.json
is not null.
In the code below, I'm using Closure Library's goog.async.ConditionalDelay
. It works, but is there a better way (maybe without needing Closure Library) to do this?
ChatRoom.prototype.json = null; // received after a long-running HTTP request.
ChatRoom.prototype.render = function() {
var thisChatRoom = this;
function onReady() {
console.log("Received JSON", thisChatRoom.json);
// Do rendering...
}
function onFailure() {
alert('Sorry, an error occurred. The chat room couldn\'t open');
}
function isReady() {
if (thisChatRoom.json != null) {
return true;
}
console.log("Waiting for chat room JSON...");
return false;
}
// If there is a JSON request in progress, wait until it completes.
if (isReady()) {
onReady();
} else {
var delay = new goog.async.ConditionalDelay(isReady);
delay.onSuccess = onReady;
delay.onFailure = onFailure;
delay.start(500, 5000);
}
}
Note that "while (json == null) { }" isn't possible because that would be synchronous (blocking all other JS execution).
Consider this:
(function wait() {
if ( chatroom.json ) {
chatroom.render();
} else {
setTimeout( wait, 500 );
}
})();
This will check every half second.
Live demo: http://jsfiddle.net/kBgTx/
You could also achieve this using lodash's debouncer with recursion.
import _debounce from 'lodash/debounce';
const wait = (() => {
if ( chatroom.json ) {
chatroom.render();
} else {
_debounce(wait, 500)();
}
})();
we can use await new Promise((resolve) and call resolve when our condition is true : we can run whole code in browser with audioPermission On.
(async () => {
const getVolume = async () => {
let n = 0;
let abc = async () => {
let count = 0;
try {
let audioStream = await navigator.mediaDevices.getUserMedia({
audio: {
echoCancellation: true,
},
});
let audioContext = new AudioContext();
let audioSource = audioContext.createMediaStreamSource(audioStream);
let analyser = audioContext.createAnalyser();
analyser.fftSize = 512;
analyser.minDecibels = -127;
analyser.maxDecibels = 0;
analyser.smoothingTimeConstant = 0.4;
audioSource.connect(analyser);
let volumes = new Uint8Array(analyser.frequencyBinCount);
console.log("volume__________________");
console.log(volumes);
await new Promise((resolve) => {
const timer = setInterval(() => {
count += 1;
console.log("count = " + count);
analyser.getByteFrequencyData(volumes);
console.log("volume__________________");
console.log(volumes);
if (count == 3 || volumes[0] > 0) {
clearInterval(timer);
n = volumes[0];
console.log("count__________________" + count);
resolve(n);
}
}, 2000);
});
} catch (e) {
console.error(
"Failed to initialize volume visualizer, simulating instead...",
e
);
return 0;
}
console.log("n inside______________ = " + n);
return n;
};
await abc();
return n;
};
let t = await getVolume();
console.log(t);
})();
The answer I came up with is like this:
var count = 0;
// Number of functions that need to run. This can be dynamically generated
// In this case I call check(data, cb) a total of 3 times
var functionNum = 3;
function toCallAfter(){
console.log('I am a delayed function');
}
I had this for a check function that ran once regularly and twice in a loop:
check(data, function(err){ // check is my asynchronous function to check data integrity
if (err){
return cb(null, { // cb() is the return function for the containing function
errCode: 'MISSINGINFO',
statusCode: 403,
message : 'All mandatory fields must be filled in'
});
} // This part here is an implicit else
count++; // Increment count each time required functions complete to
// keep track of how many function have completed
if (count === functionNum) {
return anon();
}
return;
});
// Run twice more in a loop
for(var i = 0; i < 2; i++) {
check(data, function(err) { // calls check again in a loop
if (err){
return cb(null, {
errCode: 'MISSINGINFO',
statusCode: 403,
message : 'All mandatory fields must be filled in'
});
}
count++;
if (count === functionNum) {
return toCallAfter();
}
return;
});
}
Lastly, I'd like to point out a significant performance fault in the alternate (and extraordinarily common) answer:
(function wait() {
if ( chatroom.json ) {
chatroom.render();
} else {
setTimeout( wait, 500 );
}
})();
In this case, you are essentially holding the browser or server (If using node.js) hostage for 500 milliseconds for every check which is an incredibly long time for a computer. Meaning a huge performance hit. My solution of directly keeping track of the required completed functions is free from time restraints and will run instantly as soon as all functions complete.
精彩评论