Let's say there are two async functions:
async function asyncCall() {
for(var i=0;i<50000;i++){
console.log('1');
}
}
async function asyncCall2() {
for(var i=0;i<5;i++){
console.log('2');
}
}
And I call both functions:
asyncCall();
asyncCall2();
This is the output:
1 50.000 times, then 2 one time.
Shouldn't the 2 be logged somewhere between 50.000 and 1 time?
There is no threading in JavaScript. Since all your code is just executed in the one single thread of js, the output is expected.
Your misconception about async is quite common. You think that async implies some kind of "background work" but it is not! There is no threading involved in async! Within an async function you can just say "I did what I want to do, but know I need to wait.. maybe later I can carry on.. you can get the thread to do other stuff". But if other stuff started, than that other stuff is executed until it is done (or yields back the thread again).
To get your expected result you would need to yield back execution to the rest of the code from within the function - but still it is no threading and hence, if our awaited result arrived it is not processed until the currently running code is done.
function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}
async function asyncCall() {
console.log('calling');
const result = await resolveAfter2Seconds(); // we will wait and during that time the single thread executing our code can do other stuff
console.log(result);
// expected output: 'resolved'
}
function fnCall2() {
console.log('fn2');
for(var i=0;i<500;i++) { // this will not be interrupted!
console.log(i);
}
}
asyncCall();
fnCall2();
The result of asyncCall will arrive after 2 seconds, but it does not mean it is processed immediately. If fn2 needs more time than the processing of the result is delayed. Try setting fn2 to iterate to 50000 - the resolve message will always be printed afterwards.
Related
I am calling two APIs with two different functions and having a setTimeout for both, is it possible to handle their timeouts in such a way that they finish in 10s instead of 15s.
function delay(s){
return new Promise(resolve => setTimeout(resolve,s));
}
async function getXml(){
let ans;
await delay(10000)
const {data} = await axios.get('https://gist.githubusercontent.com/SwayamShah97/a3619c5828ac8ed8085c4ae295a855d9/raw/e4e372552e042bd8bd9e8ab87da93eb030114f86/people.xml');
xml2js.parseString(data, (err, result) => {
if(err) {
throw err;
}
ans = result
});
return ans;
}
async function getPeople(){
await delay(5000)
const { data } = await axios.get('https://gist.githubusercontent.com/SwayamShah97/0f2cb53ddfae54eceea083d4aa8d0d65/raw/d7d89c672057cf7d33e10e558e001f33a10868b2/people.json');
return data; // this will be the array of people objects
}
Is there a way to run this code only in 10s, so that both the APIs get called in 10s time period
Forcing either one or both axios.get() functions to complete below some time limit (other than failing on a timeout), isn't doable, since you don't control the transport.
One thing you can do is force one or more functions to complete at or after some time threshold, with something like this...
function padToTime(promise, interval) {
// delay returns a promise that resolves after an interval
const delay = interval => new Promise(resolve => setTimeout(resolve, interval));
// caller can provide a singular or an array of promises, avoiding the extra .all
let promises = Array.isArray(promise) ? promise : [promise];
return Promise.all([...promises, delay(interval)])
.then(results => results.slice(0, -1));
}
EDIT good idea from #VLAZ to append an extra promise that forces minimal time (then slice its result off later).
The caller can say:
async function getXml(){
// op function, no calls to "delay"
}
async function getPeople(){
// op function, no calls to "delay"
}
// run these both together, have them take *no fewer than* 10s
padToTime([getXml(),getPeople()], 10000).then(results => {
// we'll get here in no fewer than 10sec with the promise all results
})
I have a sample code and questions along with that
'use strict';
const AWS = require('aws-sdk');
const promiseFunction = (item) => {
const database = new AWS.DynamoDB();
const params = {
TableName: 'some-table',
Item: item
}
try {
//database.putItem(params).promise(); //3
//Updated as per the comments..
return database.putItem(params).promise(); //3
} catch (err) {
console.log(`Error`);
}
}
const test = async () => {
const arr = [];
try {
for (let data=1;data<=1000;data++) {
const obj = { data: {N: data.toString()}}
arr.push(promiseFunction(obj)); //1
}
const result = await Promise.all(arr); //2
} catch (e) {
console.log(`Error`);
}
}
test();
Some follow-up questions:
At line-2, the result will contain the resolve/reject result once all the promise based function get executed on line //1. Right?
How, the promiseFunction at line-1 is executing and inserting item in dynamodb as I am just pushing it into an array rather than calling the dynamo putItem API. Technically, all 'putItem' should start execute in parallel at line-2. Please help here?
Is the above piece of code is valid in terms of Promise.all and executing insert operations in parallel. Please advice.
At line-2, the result will contain the resolve/reject result once all the promise based function get executed on line //1. Right?
The result will be assigned once all the promises in the arr have been fulfilled. Promise.all doesn't know (or care) how those promises were created.
Technically, all 'putItem' should start execute in parallel at line-2.
No. All the putItem() calls are executed in your loop. Promise.all doesn't execute anything, it only waits for promises created earlier. The request are done concurrently because the code starts them all at once, without waiting in between each loop iteration, not because it uses Promise.all.
Is the above piece of code is valid in terms of Promise.all and executing insert operations in parallel.
No. Your promiseFunction doesn't actually return a promise, since you forgot a return statement on the line marked as //3. Once you add that, the code is fine though.
Below is a simple example of what I'm currently working with: a websocket stream which does some asynchronous calls as part of the logic when consuming the incoming data. I'm mimicking async calls with a Promise-ified setTimeout function:
function someAsyncWork() {
return new Promise(resolve => {
setTimeout(() => {
resolve('async work done');
}, 5);
});
}
async function msg() {
const msg = await someAsyncWork();
console.log(msg)
}
const main = async() => {
web3.eth.subscribe('pendingTransactions').on("data", async function(tx){
console.log('1st print: ',tx);
await msg();
console.log('2nd print: ',tx);
})
}
main();
Running the above results in a console output like so:
1st print: 0x8be207fcef...
1st print: 0x753c308980...
1st print: 0x4afa9c548d...
async work done
2nd print: 0x8be207fcef...
async work done
2nd print: 0x753c308980...
async work done
2nd print: 0x4afa9c548d...
.
.
.
I get what's happening here. The '1st print' is executed, followed by await-ing the async calls for each piece of data response. The '2nd print' is only executed after the 'async work done' occurs.
However this isn't quite what I'm looking for.
My logic has conditionals in place, where each data response will first use a global variable to check for a condition, followed by some async work if condition is met. Problem is that there are instances where some data responses will go ahead and execute async work when they shouldn't have: Nodejs's event loop hasn't had a chance to transfer some previous data response's async calls from the callback queue to the call stack, as the stack was too busy processing new incoming data. This means the '2nd prints' haven't executed (where the global variable is updated) before new incoming data has been processed. I imagine the someAsyncWork is only resolved when there is a free period in the websocket with no data incoming.
My question is: is there a way to ensure full, sequential processing of each piece of new data? Ideally the console output would look something like this:
1st print: 0x8be207fcef...
async work done
2nd print: 0x8be207fcef...
1st print: 0x753c308980...
async work done
2nd print: 0x753c308980...
1st print: 0x4afa9c548d...
async work done
2nd print: 0x4afa9c548d...
.
.
.
You can have a queue-like promise that keeps on accumulating promises to make sure they run sequentially:
let cur = Promise.resolve();
function enqueue(f) {
cur = cur.then(f);
}
function someAsyncWork() {
return new Promise(resolve => {
setTimeout(() => {
resolve('async work done');
}, 5);
});
}
async function msg() {
const msg = await someAsyncWork();
console.log(msg);
}
const main = async() => {
web3.eth.subscribe('pendingTransactions').on("data", function(tx) {
enqueue(async function() {
console.log('1st print: ',tx);
await msg();
console.log('2nd print: ',tx);
});
})
}
main();
function sendMessage(){
...
if(file){
sendingMessage = true;
setTimeout(() => {
sendingMessage = false;
messages = [...messages, chatmessage];
}, 3800)
}
chatmessage = '';
inputRef.focus()
updateScroll();
}
Right now when this function is called, chatmessage is first set to an empty string, etc. then the code in the timeout function is executed which is the corect behaviour. However, is there a way make setTimeout synchronous?
This is the behaviour I want:
If if(file) is true, set sendingMessage = true (sendingMessage triggers a spinning wheel animation)
Wait for 3.8 seconds and execute the content in the timeout function.
Only then, set chatmessage to empty string, etc (rest of the code)
Edit 1:
I can just move the code inside the timeout function but the issue is in my case, if file is not present, it should just skip the timeout function alltogether. Then I would have to write another if block to check if the file is not present to execute the same commands (duplicate code). Another way to solve this would be to put the same code in a function and call it both places but feel like its not the ideal way to go and creating a function to change the value of three variables seems like overkill.
You could make use of a Promise to wait for 3 seconds before executing the code.
For this to work, create a function that returns a promise which resolves after 3 seconds.
function wait(seconds) {
return new Promise(resolve => {
setTimeout(resolve, seconds * 1000);
});
}
Then inside the sendMessage function, call this function, passing in the number of seconds you want to wait. When the promise returned by this wait function resolves, execute the code that you want to execute after wait time is over.
Following code shows an example of how you coulc call wait function inside sendMessage function.
async function sendMessage() {
...
if(file){
sendingMessage = true;
await wait(3); // wait for 3 seconds
sendingMessage = false;
messages = [...messages, chatmessage];
}
chatmessage = '';
inputRef.focus()
updateScroll();
}
You can use promise here, your example is to complex, so I will show smaller
function sleep(time) {
return new Promise(resolve=>setTimeout(resolve, time));
}
async function run() {
console.log(1);
await sleep(1000);
console.log(2);
}
run();
You can't achieve this directly. You have to wrap your timeout function into a promise and await for the result inside an async function.
let file = true
function asyncTimeout(resolver){
return new Promise(resolver)
}
async function sendMessage(){
console.log("Before setTimeout")
if(file){
await asyncTimeout((resolve, reject) => {
setTimeout( function() {
console.log("Timeout finished!")
resolve()
}, 3800)
})
}
console.log("After setTimeout")
}
sendMessage()
Here is a fiddle:
https://jsfiddle.net/38rwznm6/
I have an RxJS sequence being consumed in the normal manner...
However, in the observable 'onNext' handler, some of the operations will complete synchronously, but others require async callbacks, that need to be waited on before processing the next item in the input sequence.
...little bit confused how to do this. Any ideas? thanks!
someObservable.subscribe(
function onNext(item)
{
if (item == 'do-something-async-and-wait-for-completion')
{
setTimeout(
function()
{
console.log('okay, we can continue');
}
, 5000
);
}
else
{
// do something synchronously and keep on going immediately
console.log('ready to go!!!');
}
},
function onError(error)
{
console.log('error');
},
function onComplete()
{
console.log('complete');
}
);
Each operation you want to perform can be modeled as an observable. Even the synchronous operation can be modeled this way. Then you can use map to convert your sequence into a sequence of sequences, then use concatAll to flatten the sequence.
someObservable
.map(function (item) {
if (item === "do-something-async") {
// create an Observable that will do the async action when it is subscribed
// return Rx.Observable.timer(5000);
// or maybe an ajax call? Use `defer` so that the call does not
// start until concatAll() actually subscribes.
return Rx.Observable.defer(function () { return Rx.Observable.ajaxAsObservable(...); });
}
else {
// do something synchronous but model it as an async operation (using Observable.return)
// Use defer so that the sync operation is not carried out until
// concatAll() reaches this item.
return Rx.Observable.defer(function () {
return Rx.Observable.return(someSyncAction(item));
});
}
})
.concatAll() // consume each inner observable in sequence
.subscribe(function (result) {
}, function (error) {
console.log("error", error);
}, function () {
console.log("complete");
});
To reply to some of your comments...at some point you need to force some expectations on the stream of functions. In most languages, when dealing with functions that are possibly async, the function signatures are async and the actual async vs sync nature of the function is hidden as an implementation detail of the function. This is true whether you are using javaScript promises, Rx observables, c# Tasks, c++ Futures, etc. The functions end up returning a promise/observable/task/future/etc and if the function is actually synchronous, then the object it returns is just already completed.
Having said that, since this is JavaScript, you can cheat:
var makeObservable = function (func) {
return Rx.Observable.defer(function () {
// execute the function and then examine the returned value.
// if the returned value is *not* an Rx.Observable, then
// wrap it using Observable.return
var result = func();
return result instanceof Rx.Observable ? result: Rx.Observable.return(result);
});
}
someObservable
.map(makeObservable)
.concatAll()
.subscribe(function (result) {
}, function (error) {
console.log("error", error);
}, function () {
console.log("complete");
});
First of all, move your async operations out of subscribe, it's not made for async operations.
What you can use is mergeMap (alias flatMap) or concatMap. (I am mentioning both of them, but concatMap is actually mergeMap with the concurrent parameter set to 1.) Settting a different concurrent parameter is useful, as sometimes you would want to limit the number of concurrent queries, but still run a couple concurrent.
source.concatMap(item => {
if (item == 'do-something-async-and-wait-for-completion') {
return Rx.Observable.timer(5000)
.mapTo(item)
.do(e => console.log('okay, we can continue'));
} else {
// do something synchronously and keep on going immediately
return Rx.Observable.of(item)
.do(e => console.log('ready to go!!!'));
}
}).subscribe();
I will also show how you can rate limit your calls. Word of advice: Only rate limit at the point where you actually need it, like when calling an external API that allows only a certain number of requests per second or minutes. Otherwise it is better to just limit the number of concurrent operations and let the system move at maximal velocity.
We start with the following snippet:
const concurrent;
const delay;
source.mergeMap(item =>
selector(item, delay)
, concurrent)
Next, we need to pick values for concurrent, delay and implement selector. concurrent and delay are closely related. For example, if we want to run 10 items per second, we can use concurrent = 10 and delay = 1000 (millisecond), but also concurrent = 5 and delay = 500 or concurrent = 4 and delay = 400. The number of items per second will always be concurrent / (delay / 1000).
Now lets implement selector. We have a couple of options. We can set an minimal execution time for selector, we can add a constant delay to it, we can emit the results as soon as they are available, we can can emit the result only after the minimal delay has passed etc. It is even possible to add an timeout by using the timeout operators. Convenience.
Set minimal time, send result early:
function selector(item, delay) {
return Rx.Observable.of(item)
.delay(1000) // replace this with your actual call.
.merge(Rx.Observable.timer(delay).ignoreElements())
}
Set minimal time, send result late:
function selector(item, delay) {
return Rx.Observable.of(item)
.delay(1000) // replace this with your actual call.
.zip(Rx.Observable.timer(delay), (item, _))
}
Add time, send result early:
function selector(item, delay) {
return Rx.Observable.of(item)
.delay(1000) // replace this with your actual call.
.concat(Rx.Observable.timer(delay).ignoreElements())
}
Add time, send result late:
function selector(item, delay) {
return Rx.Observable.of(item)
.delay(1000) // replace this with your actual call.
.delay(delay)
}
Another simple example to do manual async operations.
Be aware that it is not a good reactive practice ! If you only want to wait 1000ms, use Rx.Observable.timer or delay operator.
someObservable.flatMap(response => {
return Rx.Observable.create(observer => {
setTimeout(() => {
observer.next('the returned value')
observer.complete()
}, 1000)
})
}).subscribe()
Now, replace setTimeout by your async function, like Image.onload or fileReader.onload ...