My understanding of the async handling in nodejs/javascript is that if an async call is handled in a function it must return a promise or accept a callback for chaining as well as for waiting for the async call to get complete.
But I just find out it does not, as the following code works and wait for all the promises to get completed
function handlePromise() {
Promise.resolve('Hello Async').then(data => {
Promise.resolve('Hello Async 2').then(data => {
return delay(3000).then(() => console.log(data));
});
return delay(2000).then(() => console.log(data));
});
Promise.resolve('hello').then(data => console.log(data))
};
function delay(time) {
return new Promise(resolve => setTimeout(resolve, time))
}
function handlePromise2() {
handlePromise()
};
handlePromise2();
It should work when I return the promises up to the end.
function handlePromise() {
return Promise.resolve('Hello Async').then(data => {
return Promise.resolve('Hello Async 2').then(data => {
return delay(3000).then(() => console.log(data));
});
}).then(() => {
return Promise.resolve('hello').then(data => console.log(data))
}).then(() => {
return Promise.resolve('hello').then(data => console.log(data))
});
};
function delay(time) {
return new Promise(resolve => setTimeout(resolve, time))
}
function handlePromise2() {
return handlePromise()
};
handlePromise2();
Same works with the callbacks also
const fs = require('fs')
function handlePromise() {
delay();
console.log('sync')
};
function delay() {
fs.writeFile('data.txt', 'Hello Async', () => console.log('done'));
}
handlePromise();
So what's I am missing out here?
If just calling a then with the promise resolves the promise then whats the point of async/await if I dont need the resolved value?
If i understand your question right, you're saying that the first snippet should not work because it's not returning the promises and you're asking why is it working.
Short answer: It's not really working, handlePromise2() finished and returned without waiting on the promises to get resolved or rejected.
Long answer: It's like you going to the bakery and asking for a bread, but instead of waiting in line after asking for it you leave, the bread still gets baked but then it gets thrown away because the client (In our case that's handlePromise2) made the call and assumed that the work is finished, after all its scope was to just call that function.
Promises are used so that the client that's calling the function knows to expect something, so after you'd ask for the bread you'd wait for it to be finished, and that's called a promise it's not the actual value (aka bread) but a promise of a value.
That's what you're doing in the second snippet.
And now poor handlePromise() doesn't know what to do with the food
Related
I need to make two post requests in succession. There is an async "post form" function which starts the request with the "await" keyword and the result is handled in the "then" block.
I try to do this post request by awaiting a promise, wherein I call the above mentioned async function. The problem is that it doesn't seem that the promise can be resolved inside the functions "then" block.
I have some example code to show the issue (look at start() function where it starts). See fiddle
//Returns a promise. It contains another async-await promise
function mainFunction() {
return new Promise( () => { //<-- this never resolves
async function postForm(){
//(Post a form)
await new Promise( (resolve, reject) => {
setTimeout(() => {
resolve('resolved');
}, 2000);
}).then( response => { // <-- how to resolve the first promise inside here?
console.log('promise 2 response: ',response)
//Try to resolve the 1st promise (doesn't work)
new Promise( (resolve,reject) => resolve('promise 1 resolved') );
}).catch( error => {
console.log(error)
})
}
postForm();
})
}
//Make two posts in succession
async function start(){
//Post 1 - (never resolves)
await mainFunction().then( response => {
//(Next: make post request 2)
}).catch( error => {
console.log(error)
})
}
start();
How can I resolve the first promise, or isn't it possible? Is there some other solution?
The idea is to make another post request when the first one is resolved.
If you use promises then you work with .then() and .catch() if you work with async/ await then you dont use .then() and .catch() because async / await is syntatic sugar so that we dont need to nest our responses over and over.
If you want to make 2 post request for example with axios or fetch then you can simply use async / await because they return an promise.
async function requests(){
//await makes the request wait till it gets some response
const response1 = await axios.post("url", {data});
const response2 = await axios.post("url2", {data});
}
I'm working on Ionic v4 with Angular.
In my project i use the BLE to communicate with a raspberry.
I have several step :
Search Device around me
Connect to this device
Activate Notification
Send Messages
Currently i have something like :
this.ble.scan().subscribe(result => {
if (device === theDeviceIWant) {
this.ble.connect(device.id).subscribe(result => {
this.ble.startNotification(infosaboutDevice).subscribe(result => {
// Message 1
this.ble.writeWithoutResponse(infos, message).then(result => {
// Message 2
this.ble.writeWithoutResponse(infos, message).then(result => {
// Message 3
this.ble.writeWithoutResponse(infos, message).then(result => {
// Message X
this.ble.writeWithoutResponse(infos, message).then(result => {
})
})
})
})
})
})
})
}
I want to do something like that :
this.myScan();
this.myConnect();
this.myNotification();
this.myMessage('Text 1');
this.myMessage('Text 2');
this.myMessage('Text X');
The probleme : My function ‘myConnect‘ don't wait the end of ‘myScan‘ to start. So somme stuff needed by ‘myConnect‘ is do in ‘myScan‘.
I already try to use ‘async/await‘ but does not work. I think i don't use it correctly :
await this.myConnect().then(async () => {
await this.myNotification().then(async () => {
await this.myMessage('03020000').then(async () => {
await this.myMessage('010100').then(async () => {
await this.myMessage('020200' + this.random.toString(16));
});
});
});
});
Help me to understand how to create a function who wait the end of the before one to start :D
Just use async/await OR then
await this.myConnect(); // this awaits the Promise returned by myConnect to be resolved
await this.myNotification(); // same for this Promise
await this.myMessage('03020000'); // and so on...
await this.myMessage('010100');
await this.myMessage('020200' + this.random.toString(16));
The keyword await makes JavaScript wait until that promise settles and
returns its result.
So you dont need to use then in await this.myConnect().then(()=>{});
use await this.myConnect();
Below is example which help you understand better
function SignalOne() {
return new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('Hello iam signal one');
}, 2000);
});
}
function SignalTwo() {
return new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('Hello iam signal Two');
}, 1000);
});
}
async function sendSignal() {
let one = await SignalOne();
let two = await SignalTwo();
console.log(one);
console.log(two);
}
sendSignal();
Try this:
async myScan() {
// do things
}
ngOnInit() {
const scan = this.myScan(); // myScan doesn't actually have to return here
await scan;
const connect = this.myConnect();
await connect;
// more stuff
}
This is essentially what Promises are made for.
A Promise is an object representing the eventual completion or failure
of an asynchronous operation.
You can read up about Promises here. Once you read thru that, I left an example for you below to demonstrate how to use a Promise:
//Wrap the operation you want to wait for in a Promise (in this case: setTimeout)
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('3 seconds have passed');
}, 3000);
});
//Once the operation is resolved the callback in the .then will be called
promise.then(val => document.querySelector('#target').innerHTML = val);
<div id="target">This message will change once the operation is resolved in 3 seconds.</div>
I would embrace Observables. Looking at what you want..
Search Device around me
Connect to this device
Activate Notification
Send Messages
1 and 2 would be chained with switchMap, as responses depend on each other. Then 3 and 4 could be performed in order, but not dependent on each other, therefore we could use concat with those. (If this is not correct flow, adjust accordingly with these two operators).
So I suggest the following:
import { never } from 'rxjs';
import { switchMap, concat } from 'rxjs/operators';
// ...
this.ble.scan().pipe(
switchMap((device) => {
if (device === theDeviceIWant) {
return this.ble.connect(device.id)
}
// terminates rest of code
return never();
}),
concat(
this.ble.startNotification(...),
this.ble.writeWithoutResponse(...)
)
).subscribe(data => console.log(data))
You're so close! Rather than using .then and async just use one or the other. Here are a few ways to accomplish what you are trying to do:
Using .then:
This is your typical chaining syntax. Promises can be chained using .then() and passing in a function. If the return value is a value (not a Promise) then it will resolve to that value. But if it did return a Promise then it will chain together and your next .then() will resolve to the "inner" async call result.
// Message 1
return this.ble.writeWithoutResponse(infos, message).then(result1 => {
// Message 2
return this.ble.writeWithoutResponse(infos, message);
}).then(result2 => {
// Message 3
return this.ble.writeWithoutResponse(infos, message);
)}.then(result3 => {
// Message X
return this.ble.writeWithoutResponse(infos, message);
}).then(result4 => { })
Using async/await
This approach achieves the same result but uses special keywords to automatically chain promises together. async/await allows you to skip the .then() and return calls so you can invoke your async functions as if they were synchronous.
// Message 1
let result1 = await this.ble.writeWithoutResponse(infos, message)
// Message 2
let result2 = await this.ble.writeWithoutResponse(infos, message);
// Message 3
let result3 = await this.ble.writeWithoutResponse(infos, message);
// Message X
let result4 = await this.ble.writeWithoutResponse(infos, message);
To learn more about Promise's and async javascript, check out these resources:
Promises on MDN
Promises on Google Web Fundamentals
Video on Async/Await
Let's say I have the following async function. The console.log will only fire after it has been resolved.
(async () => {
await new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
});
console.log('resolved');
})();
Is this valid JavaScript? Or should I always use .then()? What are the drawbacks or behavioral differences from using .then() here?
(async () => {
return await new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
});
})().then(() => {
console.log('resolved');
});
Off-topic: I also noticed the latest method return await can be changed to just return without the results changing, but this thread answered that question.
Being able to handle asynchronous operations without always resorting to then is the main point of async/await. The first example is perfectly valid.
The second one is a bit of an antipattern as it would be functionally identical without the async/await for two reasons - marking your function with async implicitly makes it return a Promise, but you also explicitly return one - and awaiting the Promise causes the code to wait until the Promise resolves before returning it, but since you are chaining with then, the then doesn't run before the Promise resolves anyway.
This would be functionally identical:
(() => {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
});
})().then(() => {
console.log('resolved');
});
The main drawback of using "synchronous-like" code like the first example is error handling - if you do 5 await operations in a row and any of them reject, your whole async function returns a rejected Promise. If you chain then operations, you can also insert catch handlers to handle specific error cases with more precision.
Promises just baffle me.
I'm trying to make a mock data service to imitate axios.
My mock put call passes a targetUrl to _fetch which then sees if it's a valid url and either returns a new Promise with a delayed .resolve
const _returnResponse = (mockData, time = 0) => new Promise((resolve) => {
setTimeout(() => {
resolve(mockData);
}, time);
});
or a new Promise with a delayed .reject
const _returnError = (time = simulatedDelay) => {
const returnValue = new Promise(((resolve, reject) => {
setTimeout(() => {
reject(new Error('error'));
}, time);
}));
return returnValue;
};
but when I make my mock put call this returns a mock data that the calling method interprets as a success and console logs in its .then
put(target, putBody) {
const returnValue = _fetch(target, simulatedDelay)
returnValue.then(response => _console('PUT', target, response, putBody));
return returnValue;
},
But with an invalid target console logs an uncaught error
or this handles the error correctly, but console logs an undefined response
put(target, putBody) {
const returnValue = _fetch(target, simulatedDelay).then(response => _console('PUT', target, response, putBody));
return returnValue;
},
Here's the calling method:
saveStuff({ commit, state }, newStuff) {
//other code
return this.$mockAxios.put(url, putBody)
.then((response) => {
return response;
});
},
I feel like I'm completely missing something and I've researched this for hours and I'm still not getting it.
As a direct answer to the question: yes, you can add .then() to a promise after it's created.
Example:
const hi = new Promise((resolve, reject) => {
setTimeout(() => resolve('hello'), 2000);
});
hi.then(result => console.log(result));
As for promises baffling you, I would recommend (aside from more reading) just playing around a lot in your IDE with setTimeout. I know you're already using setTimeout in your mock, but strip it down further and just run the code in its own file, so that you control the whole environment. With lots of console.log('blah') to see the order and such. Also ensure you're just as familiar with callbacks, as promises are just syntactic sugar for callbacks. Try to read up on the JS event loop at the same time - it might provide some context if you know how and when a callback/promise executes.
Note that you can even add .then() after the callback has resolved or rejected. Hence the term "promise" - it's literally a promise that your .then() function will run. https://en.wikipedia.org/wiki/Promise_theory
const hi = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('one second');
resolve();
}, 1000);
});
setTimeout(() => {
hi.then(() => console.log('two seconds. this executes approximately a full second after the first promise has resolved'));
}, 2000);
Let a function which returns a promise:
async function foo() {
return await new Promise((res, rej) => {
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(res => res.json())
.then(data => res(data))
.catch(err => rej(err))
})
}
In order to reuse the returned data I can think something like:
(async function() {
data = await foo().catch(err => console.log('err: ', err))
fnc1(data)
fnc2(data)
...
fncn(data)
})();
or something like:
foo().then(data => {
fnc1(data)
fnc2(data)
...
fncn(data)
}
)
So always I have to go back in my code find the function or callback which gets the data returned by the promise and include any new function that I want to the appropriate block.
I was wondering if there is any smarter way to achieve the same result in javascript.
I know that something like the following won't work:
var dataFromPromise
foo().then(data => dataFromPromise = data)
console.log(dataFromPromise) //undefined if called before foo runs
Don't wrap promises inside promises
fetch already returns a Promise, so there is no need to wrap it into another Promise. Instead of:
async function foo() {
return await new Promise((res, rej) => {
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(res => res.json())
.then(data => res(data))
})
}
Do:
function foo(){
return fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(res => res.json())
.then(data => res(data))
.catch(err => rej(err))
}
Reusing promises
Once you have a Promise, should it be pending, rejected or fulfilled, you can reuse it again and again, and if it is resolved it will always give the returned value. So instead of:
foo().then(data => {
fnc1(data)
fnc2(data)
...
fncn(data)
}
)
You can just do:
const myPromise = foo();
myPromise.then(data => fnc1(data));
// some code
myPromise.then(data => fnc2(data));
// more code
myPromise.then(data => fnc3(data));
And you can be sure that fnc1 and the others won't get call until the promise resolves.
Now, this sure is good, but doesn't solve all the related problems, and a lot of bad things can still happen with this approach. To tackle all the possible ways to handle and reuse promise is something too wide for a SO answer.
A pretty good resource about this topic is:
http://2ality.com/2017/08/promise-callback-data-flow.html
Store the promise once somewhere:
const dataPromise = foo();
Then whenever you need to call a function that uses it:
dataPromise.then(fnc1)
Or inside async functions:
fnc1(await dataPromise)