Does an async await Promise resolve require .then() to be called? - javascript

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.

Related

Why my custom observable behaviour is different when i transform it to Promise?

I have the following observables
callOne() {
return new Observable(subscriber => {
subscriber.next(true);
setTimeout(() => {
subscriber.complete();
}, 3000)
})
}
callTwo() {
return new Observable(subscriber => {
subscriber.next(false);
setTimeout(() => {
subscriber.complete();
}, 1000)
})
}
so when i call subscribe to them
this.callOne().subscribe(data => {
console.log(data);
})
this.callTwo().subscribe(data => {
console.log(data);
})
i get immediately true and false printed, even that i setted complete method in the setTimeout.
With that i am saying that in x miliseconds there can't be emitted new values after the execution of the complete methhod.
When i try the same but this time the observables are converted into promises
let response1 = await this.callOne().toPromise();
console.log('response1', response1);
let response2 = await this.callTwo().toPromise();
console.log('response2', response2);
then i get printed true from the callOne observable in 3000 miliseconds.
Why is that ?
Why when i have promise the complete method is taken into consideration with the setTimeout
but with the observable it is not ?
you should change something in your code. You have to run next method in setTimout. because when you run next method observables triggered and also you subscribe an observable it will invoke.
callOne() {
return new Observable(subscriber => {
setTimeout(() => {
subscriber.next(true); // you can use like this
subscriber.complete();
}, 6000)
})
}
But when you use await it is working like then() and it will invoke when everything is done like complete.
I would imagine the promise does not resolve until the subscriber.complete() call. Since you used await, execution will not continue until the promise resolves.
This article actually explains that the promise returned from toPromise() waits for the observable to complete, then returns the last value: https://levelup.gitconnected.com/rxjs-operator-topromise-waits-for-your-observable-to-complete-e7a002f5dccb
toPromise() is deprecated though, so I wouldn't bother using it. You can just wrap Observables in Promises instead. Then you can control exactly when they resolve.
callOne() {
const observable = new Observable((subscriber) => {
subscriber.next(true);
setTimeout(() => {
subscriber.complete();
}, 3000);
});
return new Promise((resolve) => {
observable.subscribe((result) => resolve(result));
});
}
This promise will resolve on the first call of subscriber.next() which I believe is what you were expecting to happen.
Keep in mind Observables can call .next() repeatedly, but Promises can only resolve once.
Observables and promises are different things even though they might convert one into another.
What you are facing is expected. The promise does not "resolve" until the observable completes, so only after completion it run the logic, and that is how the promises work. Observable in that sense are different.
In fact, if you do not complete the stream, in the subscribe you still getting the code run, but in the promise as the stream never completes, it won't run anything. A very common mistake is to convert into promise from long-life hot observables.

Timeout exceeded for Mocha promises

I have a async function that awaits a promise which resolves when it receives some 'data'. However, when I run the test, I get a Error: Timeout of 300000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
Here is my code snippet, I am using this in truffle to test solidity contracts :
contract("Test", async (accounts) => {
it("test description", async () => {
let first = await getFirstEvent(oracle.LogResult({fromBlock:'latest'}));
let second = await getFirstEvent(oracle.LogResult({fromBlock:'latest'}));
Promise.all([first,second]);
//some assertion code
});
const getFirstEvent = (_event) => {
return new Promise((resolve, reject) => {
_event.once('data', resolve).once('error', reject)
});
}
});
Isn't the promise resolving ? I can see 'data' coming back in the callback because I am emitting the callback event in the solidity code I am testing.
I managed to resolve this issue, so posting it here so that others can use the approach.
I created a Promise that times out after a duration we can set :
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {a
reject(new Error('Request timed out'));
}, 200000);
})
Then, I race the timeoutPromise with the Promise which is fetching data, like this for the case I posted :
Promise.race([getFirstEvent(oracle.LogResult({fromBlock:'latest'})), timeoutPromise]);
It looks to me like there's a few things wrong here.
First of all, your function isn't returning anything i.e. it should be return Promise.all([first, second]);.
Secondly, if the goal of the promise.all is to execute the promises in parallel, then that's not what it's doing here because you already have await statements on those function calls above. What you are looking for here would be:
return await Promise.all([
getFirstEvent(oracle.LogResult({fromBlock:'latest'}),
getFirstEvent(oracle.LogResult({fromBlock:'latest'})]);
Now in terms of the promise not resolving, I'm assuming the event is generated from oracle.LogResult(). In this case, what you'd want to do is setup your promises to listen for the event first, for example:
let first = getFirstEvent();
let second = getSecondEvent();
Now you have 2 promises that are listening for the events. Next, you generate the event:
oracle.LogResult({ fromBlock: 'latest' });
oracle.LogResult({ fromBlock: 'latest' });
Finally, you ensure you wait on the result of the promises:
return await Promise.all([first, second]);

Wait the end of a function before to start the next

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

Promise waits to get resolve without return

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

Can you add a .then to a promise after it's created?

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);

Categories