This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I want to return the status and store its value in variable s.
I will appreciate some help please.
here is my code:
let s = setTimeout( ()=>{
this.matchService.getMatches().subscribe(ms => {
this.matches = ms;
let match = this.matches.find(match => match.id == id);
let status = match.status;
if(status == 'closed' || status == 'live') {
this.status.name = status;
}
return status;
});
},9000);
}
This answer here is specifically for the setTimeout question. If you work with an observable, consider the answer of bambam!
Ok, this answer might be a little weird if you don't know the concept of async.
Basically the easiest is to wrap your setTimeout into a Promise, like so:
const someTimeoutAction = () => {
// Let's return a new Promise, promising to eventually return a value
return new Promise((resolve) => {
setTimeout(() => {
resolve('hello world');
}, 1000);
});
};
// The following function is an async function, that will
// help to work better with Promises.
const mainFunction = async () => {
console.log('Waiting for status');
const status = await someTimeoutAction();
console.log(status);
};
mainFunction();
So what happens here?
The mainFunction is called and calls someTimeoutAction.
The someTimeoutAction returns a promise. The old syntax looks a bit different. This medium article document should be a good starting point.
mainFunction waits for the Promise to resolve. After a second it is resolved and the value is written to status.
Everything else just continues now like usual.
The code above only works for modern browsers. It does not work without a transpiler for e.g. IE11.
However, the old syntax works just fine:
function someTimeoutAction() {
// Let's return a new Promise, promising to eventually return a value
return new Promise((resolve) => {
setTimeout(() => {
resolve('hello world');
}, 1000);
});
};
// The following function is an async function, that will
// help to work better with Promises.
function mainFunction() {
console.log('Waiting for status');
someTimeoutAction()
.then(function(status) {
console.log(status);
});
};
mainFunction();
Since you already have an observable, simply delay it instead of using setTimeout! Also the promise approach from the other answer is nonsense for this scenario.
this.matchService.getMatches()
.pipe(delay(9000))
.subscribe(ms => {
this.matches = ms;
let match = this.matches.find(match => match.id == id);
let status = match.status;
if(status == 'closed' || status == 'live') {
this.status.name = status;
}
});
The bigger problem with your code is that you would never never return from subscribe. Actually you would rather delay the (I'm guessing) http call in matchService, but you didn't show the relevant code.
Note that a subscription may get triggered multiple times, again depending on what getMatches() is. You're on the wrong path here and should update your question so we can tailor you a real solution.
Related
I have the following code which is being called after the native player.loadNewVideoById function from youtube.
function initSliderAndTime() {
setTimeout(() => {
var durationInSeconds = player.getDuration()
$('#time-slider').attr('max', player.getDuration().toString().match(/^-?\d+(?:\.\d{0,1})?/)[0])
$('#end-time')[0].innerText = calculateTimeFormat(durationInSeconds);
}, 1000);
}
If I don't call a time out the player id is undefined and the function ins't doing what it it supposed to do. How can I turn this into an asynchronous function, so that it executes after the player has initialised.
I know about the OnReady Function from youtube's iframe api, but that doesn't get called when the player is updated, only if it created.
Any help is appreciated!
I don't have enough sample code to test this answer atm but it should look something like this.
async function initSliderAndTime() {
let myPromise = new Promise( function( resolve ) {
resolve( player.getDuration() );
});
myPromise.then (
let maxTime = await myPromise;
$('#time-slider').attr('max', maxTime.toString().match(/^-?\d+(?:\.\d{0,1})?/)[0]);
$('#end-time')[0].innerText = calculateTimeFormat(durationInSeconds);
);
}
initSliderAndTime();
Maybe an easier more readable format is the try/catch. Either way the key is the async function declaration and the await keyword where the promise will be returned.
async function initSliderAndTime() {
try {
let durationInSeconds = await player.getDuration();
$('#time-slider').attr('max', durationInSeconds.toString().match(/^-?\d+(?:\.\d{0,1})?/)[0]);
$('#end-time')[0].innerText = calculateTimeFormat(durationInSeconds);
} catch(error) {
return null;
}
}
initSliderAndTime();
Lots more examples here: How to return the response from an asynchronous call
I have this problem in the jQuery Terminal library. I have an echo method that prints the stuff on the terminal and you can print a string, promise, or function that returns a promise or string (to simplify let's assume string or promise).
But the issue is that if you echo a few promises and strings they are not printed in order. The code was just waiting with the next echo until the promise was resolved. The problem is that it only works for one promise.
So I was thinking that I need a kind of data structure that will keep adding promises and it will wait for all promises. But I'm not sure how to do this.
The problem I have is that I can't just chain promises because the echo method needs to be synchronous when there is nothing in a queue and you print a string. But this is not how Promise A+ behaves they are always async (even Promise.resolve()). I have a lot of unit tests that rely on echo being synchronous and it will be break change and I don't want that.
My idea was to just create an array of promises but I'm not sure when I should remove the promise from the array so the queue can be empty when all promises are resolved and I can do synchronous call.
Something like:
class PromiseQueue {
constructor() {
this._promises = [];
}
add(promise) {
this._promises.push(promise);
}
empty() {
return !this._promises.length;
}
then(fn) {
if (this.empty()) {
fn();
} else {
Promise.all(this._promises).then(function() {
// what do do with this._promises?
fn();
});
}
}
}
I guess it's not that simple as in my attempt. I have no idea how to implement this behavior.
EDIT:
I have this two cases that I want to handle:
function render(text, delay) {
return new Promise(resolve => {
setTimeout(() => resolve(text), delay);
});
}
term.echo(() => render('lorem', 1000));
term.echo('foo');
term.echo(() => render('ipsum', 1000));
term.echo('bar');
term.echo(() => render('dolor', 1000));
term.echo('baz');
setTimeout(function() {
// this should render immediately because all promises
// would be already resolved after 5 seconds
term.echo('lorem ipsum');
// so after the call I check the DOM and see the output
// this is important for unit tests and will be huge
// breaking change if echo don't render string synchronously
}, 5000);
NOTE: echo promise and function that return a promise in this example is the same the only difference is that function is re-invoked in each re-render (e.g. when browser or container is resized).
Another example is just:
term.echo('foo');
term.echo('bar');
term.echo('baz');
that should be also synced. I need a generic solution so you don't need to know exactly what echo is doing.
I would not even use Promise.all here - wait only for the first promise in the queue.
const term = {
/** an array while a promise is currently awaited, null when `echo` can be synchronous */
_queue: null,
echo(value) {
if (this._queue) {
this._queue.push(value);
} else {
this._echo(value);
}
},
/** returns a promise if the `value` is asynchronous, undefined otherwise */
_echo(value) {
try {
if (typeof value == "function") {
value = value();
}
if (typeof value.then == "function") {
this._queue ??= [];
return Promise.resolve(value).then(console.log, console.error).finally(() => {
while (this._queue.length) {
if (this._echo(this._queue.shift())) {
return;
}
}
this._queue = null;
});
} else {
console.log(value);
}
} catch(err) {
console.error(err);
}
}
};
function render(text, delay) {
return new Promise(resolve => {
setTimeout(() => resolve(text), delay);
});
}
term.echo('foo');
term.echo(() => render('lorem', 1000));
term.echo('bar');
term.echo(() => render('ipsum', 1000));
term.echo('baz');
term.echo(() => render('dolor', 1000));
term.echo('quam');
setTimeout(function() {
// this should render immediately because all promises
// would be already resolved after 5 seconds
term.echo('lorem ipsum');
console.log('end');
}, 5000);
console.log('echos queued');
While I was editing the question I've realized that this is similar to exec behavior:
term.exec('timer --time 1000 "hello"');
term.exec('echo world');
term.exec('timer --time 1000 "hello"');
term.exec('echo world');
and I solve this using same mechanism that was proved to work.
I've added a flag:
if (is_promise(next)) {
echo_promise = true;
}
similar to paused flag.
Then when promise next is resolved. I used the same what was done in resume()
unpromise(next, function() {
echo_promise = false;
var original = echo_delay;
echo_delay = [];
for (var i = 0; i < original.length; ++i) {
self.echo.apply(self, original[i]);
}
});
unpromise is a function that I always use. It invokes the function as then callback or calls immediately. So it's sync by default but async when needed. You can find the code on GitHub jquery.terminal-src.js#L1072.
And then last thing in echo main code:
if (echo_promise) {
echo_delay.push([arg, options]);
} else {
echo(arg);
}
This is not very clean code because echo is invoked multiple times but it works. If you know a better solution please share.
Maybe this kind of code can be abstracted into single PromiseQueue interface.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
var count=0;
function test2(callback) {
db.doc("Kerala/Pathanamthitta")
.listCollections()
.then((snap) => {
snap.forEach((collection) => {
var col = collection.id;
db.collection(`Kerala/Pathanamthitta/${col}`)
.where("completionStatus", "<", 3)
.get()
.then((snapshot) => {
snapshot.forEach((doc) => {
var data = doc.data();
console.log(data.place);
if (data.completionStatus == 0) count++;
});
});
});
})
.then(callback);
}
test2(function () {
console.log(count);
});
I want to print the final count after the execution of test2 function. It prints the statement but always 0, even if any updation happens inside function test2. I tried to do a callback() but still the same happens
Please help. Thanks in advance
You're misunderstanding how asynchronous functions and promise chains works. You're calling a promise chain and the callback right after each other.
db.doc(...)...
callback()
This ends up executing like so:
db.doc
callback
db.doc.then
at this point you have called callback BEFORE the resolution of the promise chain. You want to put the callback in the promise chain so that it is delayed until after all of that has finished. A good spot would be in another promise chain after the outer loop for a single log of the eventual count.
...
.then(snap => {
snap.forEach(collection => {...});
})
.then(callback);
...
This way after you've finished going over all of the snaps and finished counting the snapshots you'll print out the count in the correct order after both traversals.
BUT WAIT it's still printing 0. Why is that? Well we're not properly chaining our promises. We'll need to make sure that any promises we create in a promise properly chain so that when we do get to the logging step we've got a proper chain going.
Full code:
var count = 0;
function test2(callback) {
db.doc("Kerala/Pathanamthitta")
.listCollections()
.then((snap) => {
return Promise.all(
snap.map((collection) => {
var col = collection.id;
return db
.collection(`Kerala/Pathanamthitta/${col}`)
.where("completionStatus", "<", 3)
.get()
.then((snapshot) => {
return Promise.all(
snapshot.map((doc) => {
var data = doc.data();
console.log(data.place);
if (data.completionStatus == 0) count++;
})
);
});
})
);
})
.then(callback);
}
test2(function () {
console.log(count);
});
As per my comment, you could just make a function call after you're done counting.
//content above
snapshot.forEach(doc => {
var data = doc.data();
console.log(data.place);
if (data.completionStatus == 0) count++;
});
functionTwo(count);
// content below
functionTwo(count) {
console.log(count);
}
I cannot for the life of me figure out why async/await behaves the way it does.
Consider this example.
I want a function that does some db initialization, and returns me the database object when it is done.
var globalDb = null;
const IDB_DATABASE_NAME = "mydb";
const IDB_STORE_NAME = "mystore";
const IDB_VERSION = 1.0;
async function initDb() {
var idbOpenDbRequest = window.indexedDB.open(IDB_DATABASE_NAME, IDB_VERSION);
idbOpenDbRequest.onsuccess = function (event) {
return event.target.result;
};
}
(async () => {
globalDb = await initDb();
console.log("I should happen second")
console.log(globalDb);
})();
Expected
console.log("I should happen first")
console.log("I should happen second")
console.log(globalDb); //dumps the object to console
Actual
console.log("I should happen second")
console.log(globalDb); //undefined
console.log("I should happen first")
I realize I am fundamentally misunderstanding something here. Please enlighten me why await does not work as I would expect. :)
JsFiddle
https://jsfiddle.net/p2jqyrou/2/
Ps. Forget that this is about indexedDb and that this example is extremely simplified - I don't think it matters for the topic of this question.
So the problem is with your initDb function. I'll rewrite it for you and then explain why this version does work:
function initDb() {
var idbOpenDbRequest = window.indexedDB.open(IDB_DATABASE_NAME, IDB_VERSION);
return new Promise((resolve, reject) => {
idbOpenDbRequest.onsuccess = function (event) {
setTimeout(function () {
console.log("I should happen first");
resolve(event.target.result);
}, 2000);
};
})
}
What I've done is wrap the onsuccess callback in a Promise. The async/await pattern in Javascript is fundamentally based around Promises. Either you return a Promise yourself or it wraps the result in a Promise (and immediately resolves it).
Because you did not return a Promise yourself it tried to wrap the return value (which was undefined) in a Promise. It then immediately resolved causing the async function to return.
With this updated code initDb is called which then returns a Promise which is only resolved after the connection has been made and after the timeout triggered.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I have a class getter that I want to return a value from only when an event listener has completed.
Is there a way to do this?
I am aware that I can wrap it in a Promise and apply it to the chain but it does not follow the modularisation I require the the usage of the application:
class Request{
get body(){
console.log('processed before')
console.log(processed)
this._request.on('data', chunk => {
console.log('data received');
if (null == this._body._text) {
this._body._text = "";
}
this._body._text += chunk;
})
.once('end', () => {
console.log('processed')
processed = true;
});
console.log('processed after')
console.log(processed)
console.log('return this._body')
return this._body;
}
}
let req = new Request();
console.log(req.body());
I need the return to only continue and return after the request "end" event has occurred.
Thank-you in advance.
You could generally use async functions here:
async function run(){
console.log('processed before');
console.log(processed);
var processed = await new Promise(function(processed){
this._request.on('data', chunk => {
console.log('data received');
this._body._text = "";
this._body._text += chunk;
})
.once('end', () => {
console.log('processed')
processed(true);
});
});
console.log('processed after');
console.log(processed);
console.log('return this._body')
return this._body;
}
However it is very inefficient to really halt and await execution, thats why it isnt possible in js ( await is just a beautiful .then ), and the async function actually retunrs a promise. So using it as a getter wont work and is a terrible way of doing so. You probably need a non getter approach, or the getter returns the promise:
var obj = {
get stuff(){
return run();
}
};
obj.stuff.then(console.log);
obj.stuff.then(console.log);
If modularity is a concern, you could try using events. I think a promise based workflow is probably more recommended, and idiomatic, but event based work flows can work. Essentially, you add a line to your promise that emits an event on resolution, and have your callback function listening for that event to transmit. Its the type of flow you might see in mobile apps.
That said, I think the promise approach is probably better, but if it's not workable events might be.