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
Related
This code does not execute the promise of testAuthentication or pinFileToIPFS and i am curious if this is a node concept i am not familiar of.
function uploadToPinata(filename) {
const pinata = pinataSDK(process.env.PINATA_KEY, process.env.PINATA_SECRET_KEY);
pinata.testAuthentication().then((result) => {
//handle successful authentication here
console.log(result);
}).catch((err) => {
//handle error here
console.log(err);
});
const readableStreamForFile = fs.createReadStream(filename);
const options = {
pinataMetadata: {
name: "NYC_NFT_TESTING",
},
pinataOptions: {
cidVersion: 0
}
};
pinata.pinFileToIPFS(readableStreamForFile, options).then((result) => {
//handle results here
console.log(result);
}).catch((err) => {
//handle error here
console.log(err);
});
}
is there a problem with this code using a promise within a function? I attemped to make the function async but that did not help. This code works just fine outside of a function but not within one.
"does not execute the promise": this is a strange phrase. Promises are objects. They don't execute -- they are created. And your function really creates them. However, it does not do much when these promises are resolved.
The problem is that uploadToPinata will execute all of the synchronous code and return. But then there are still a promises pending. Although you have console.log that will be executed once the relevant promise has resolved, there is no signal to the caller of uploadToPinata that these promises have resolved.
It is probably easiest to use async and await:
async function uploadToPinata(filename) {
const pinata = pinataSDK(process.env.PINATA_KEY, process.env.PINATA_SECRET_KEY);
const result = await pinata.testAuthentication();
//handle successful authentication here
console.log("authentication result", result);
const readableStreamForFile = fs.createReadStream(filename);
const options = {
pinataMetadata: { name: "NYC_NFT_TESTING" },
pinataOptions: { cidVersion: 0 }
};
const result2 = await pinata.pinFileToIPFS(readableStreamForFile, options);
//handle results here
console.log("pinFileToIPFS result", result2);
}
The caller of this function will now receive a promise, so it should probably want to do something when that promise has resolved:
uploadToPinata(filename).then(() => {
console.log("authentication and file operation done")
}).catch(error => console.log("error", error));
I'm pretty new to Node.js. I have a problem with promise functions and Promise.all.
Either I misunderstood some concept or I'm doing something wrong. I've tried researching on that topic but could not find the right answer to my specific problem.
The situation is as follows.
I have a promise function to generate a pdf, that returns the path of the generated pdf.
var myNicePdfCreator={
setting1: value1,
setting2: value2,
createPdf: function(customerId){
return new Promise(function(resolve, reject){
dataBase.fetchForId(customerId).then(function(data){
somePdfLibrary.createPdf.then(function(path){
resolve(path);
},function(err){
//error creating pdf
reject(err);
});
},function(err){
//error fetching from db
reject(err);
}
})
}
}
I have a promise function that takes an email address and path to the pdf and then sends an email with the attached pdf
var myNiceMailSender={
setting1: value1,
setting2: value2,
sendMail: function(email, path){
return new Promise(function(resolve, reject){
someMailLibrary.createMail(email, title, message, attachment).then(function(status){
resolve(status);
},function(err){
reject(err);
});
});
}
}
I want to do this for every object in an array (for example get a report for every customer and then email it to them). I was trying to come up with a promise function that first creates the pdf and then sends the mail and use a forEach loop to push the promise to an array and then use Promise.all to make all the PDFs and send out the mails. but whatever I try, whenever I push a promise to an array it already gets resolved before I even use Promise.all even if I just try pushing one of both promise functions to the array.
If i do this;
var promises=[];
AllCustomers.forEach(customer){
promises.push(myNicePdfCreator.createPdf(customer.id));
});
The PDFs are directly created when I push them to the array. I don't even need to call Promise.all.
The same if I try to push the promise function to an array that sends the emails, the mails are sent instantly.
Can anyone point me in the right direction why my promises get resolved when I push them to the array?
Is there a better way to create the PDFs and then email them?
Any help appreciated thank you!
I suppose what you want is to get all the pdfs gereated before you start sending the emails. As someone already said, when you call a Promise, unless you have a .then or an await for it, its execution is not going to wait.
const promises=[];
for(const customer of AllCustomers){
promises.push(myNicePdfCreator.createPdf(customer.id));
});
Promise.all(promises).then((paths) => {
// paths is going to have an array with the paths of all the pdfs already generated
});
With this code, in Promise.all is going to wait until all pdfs are generated. So inside the then, you could send the emails.
If you want to create an array of unresolved, unprocessing promises, which create a report for each customer and then email that customer, it would look something like this:
const pfuncs = AllCustomers.map(customer => {
return async function() {
const pdfPath = await myNicePdfCreator.createPdf(customer.id);
const status = await myNiceMailSendor.sendMail(customer.email, pdfPath);
return {status, pdfPath};
}
})
This creates an array of functions -- the 'createPdf' request hasn't started to run yet, it's waiting for you to call each function in the array.
When you're ready to send the requests, you would do
const results = await Promise.all(pfuncs.map(f => f()));
And now results is an array that looks like [{status: 200, pdfPath: ''} , {status: 200, pdfPath: ''}, ...]
The promises are executed when they are declared. If you want to "lock" all the promises till all are defined you could encapsulate in a function and after declaring all, make a loop to execute it.
// Await "a" seconds to finish, reject if "a" o "b" are negative
function myPromiseFunction(a, b) {
return new Promise((res, rej) => {
setTimeout(() => {
if (a < 0 || b < 0) {
rej(0);
} else {
res(a+b);
}
}, a * 1000);
})
}
(() => {
let someData = [{a:2,b:2}, {a:10, b:4}];
// Generate promises in functions
let myPromises = someData.map((v) => {
return () => myPromiseFunction(v.a, v.b);
});
// Execute all
myPromises = myPromises.map((promise) => promise());
// ...
})();
The Promise.all function only awaits to all the promises to finish the process or any promise is rejected. For example:
All promises good:
// Await "a" seconds to finish, reject if "a" o "b" are negative
function myPromiseFunction(a, b) {
return new Promise((res, rej) => {
setTimeout(() => {
if (a < 0 || b < 0) {
rej(0);
} else {
res(a+b);
}
}, a * 1000);
})
}
(() => {
let someData = [{a:2,b:2}, {a:10, b:4}];
// Generate promises in functions
let myPromises = someData.map((v) => {
return () => myPromiseFunction(v.a, v.b);
});
// Execute all
myPromises = myPromises.map((promise) => promise());
// Await all
Promise.all(myPromises).then(res => {
console.log(res, myPromises);
}).catch(err => {
console.log(err, myPromises);
});
})();
You will print the console.log in then and will be like this:
// res: [ 4, 14 ]
// myPromises: [ Promise { 4 }, Promise { 14 } ]
But if you have a promise who fails like this:
let someData = [{a:10,b:2}, {a:4, b:-4}, {a:2, b:4}];
The second promise will be rejected by the negative value, but the first promise will not resolve (10 seconds to end) so the output of the catch will be:
// err: 0 (this is the error of the first rejected promise)
// myPromises: [
// Promise { <pending> }, // The rejected promise will not stop the pendings
// Promise { <rejected> 0 }, // The rejected promise
// Promise { 6 }, // A "resolved" or maybe "pending"
// ]
Is there any chance to return from helpMe function value from getDataFromApi() ? So far every time i call this function i get "null" value.
async function helpMe() {
let promise = null;
let sub = someService.someObservable.subscribe(async () => {
promise = await getDataFromApi()
})
subscriptions.push(sub)
return promise;
}
The first goal is i need to store subscription in global sub array. The second goal is when i get response with status 400 - I dont want to open modal. Only when i get 200 and everything is allright i want modal to be opened.
function async load() {
const promise = await helpMe();
openModal();
}
Passing an async function to subscribe is pointless - it throws away the returned promise, it doesn't wait for anything. You would need to use
new Promise((resolve, reject) => {
someService.someObservable.subscribe(resolve, reject);
})
or just call the builtin toPromise method:
async function helpMe() {
await someService.someObservable.toPromise();
return getDataFromApi();
}
Instead of using async/await feature, you could just go with plain rxjs. The switchmap operator might help you here:
public helpMe()
{
return this.someService.someObservable.pipe(
switchMap(result =>
{
return someDataFromApi();
}),
tap(resultFromApi => {
// ... do something with `resultFromApi` from `someDataFromApi`.
}),
).toPromise();
}
I'm quite a newbie in JavaScript and in Promises.
I'm trying to build an array of objects that I get from an API.
To do so, I've build two functions in a file MyFile.js.
The first one returns a promise when an axios promise is resolved. It's
function get_items (url) {
return new Promise((resolve, reject) => {
let options = {
baseURL: url,
method: 'get'
}
axios(options)
.then(response => {
resolve(response.data)
})
.catch(error => {
reject(error.stack)
})
})
}
The second one looks like this:
let output = []
let next_url = 'https://some_url.com/api/data'
async function get_data () {
try {
let promise = new Promise((resolve, reject) => {
if (next_url) {
get_items(next_url)
.then(response => {
output.push(...response.results)
if (response.next) {
next_url = response.next
console.log('NEXT_URL HERE', next_url)
get_data()
} else {
console.log('else')
next_url = false
get_data()
}
})
.catch(error => {
reject(error.stack)
})
} else {
console.log('before resolve')
resolve(output)
}
})
return await promise
} catch(e) {
console.log(e)
}
}
It's where I'm grinding my teeth.
What I think I understand of this function, is that:
it's returning the value of a promise (that's what I understand return await promise is doing)
it's a recursive function. So, if there is a next_url, the function continues on. But if there is not, it gets called one last time to go into the else part where it resolves the array output which contains the results (values not state) of all the promises. At least, when I execute it, and check for my sanity checks with the console.log I wrote, it works.
So, output is filled with data and that's great.
But, when I call this function from another file MyOtherFile.js, like this:
final_output = []
MyFile.get_data()
.then(result => {
console.log('getting data')
final_output.push(...result)
})
it never gets into the then part. And when I console.log MyFile.get_data(), it's a pending promise.
So, what I would like to do, is be able to make get_data() wait for all the promises result (without using Promise.all(), to have calls in serie, not in parallel, that would be great for performances, I guess?) and then be able to retrieve that response in the then part when calling this function from anywhere else.
Keep in mind that I'm really a newbie in promises and JavaScript in general (I'm more of a Python guy).
Let me know if my question isn't clear enough.
I've been scratching my head for two days now and it feels like I'm running in circle.
Thanks for being an awesome community!
This is a bit untested
const api_url = 'https://some_url.com/api/data';
get_data(api_url).then((results) => {
console.log(results);
}).catch((error) => {
// console.error(error);
});
function get_items (url) {
const options = {
baseURL: url,
method: 'get'
};
return axios(options).then((response) => response.data);
}
async function get_data(next_url) {
const output = [];
while (next_url) {
const { results, next } = await get_items(next_url);
output.push(...results);
next_url = next;
}
return output;
}
Basically it makes things a bit neater. I suggest to look at more examples with Promises and the advantage and when to ease await/async. One thing to keep in mind, if you return a Promise, it will follow the entire then chain, and it will always return a Promise with a value of the last then.. if that makes sense :)
There are a few problems. One is that you never resolve the initial Promise unless the else block is entered. Another is that you should return the recursive get_data call every time, so that it can be properly chained with the initial Promise. You may also consider avoiding the explicit promise construction antipattern - get_items already returns a Promise, so there's no need to construct another one (same for the inside of get_items, axios calls return Promises too).
You might consider a plain while loop, reassigning the next_url string until it's falsey:
function get_items (baseURL) {
const options = {
baseURL: url,
method: 'get'
}
// return the axios call, handle errors in the consumer instead:
return axios(options)
.then(res => res.data)
}
async function get_data() {
const output = []
let next_url = 'https://some_url.com/api/data'
try {
while (next_url) {
const response = await get_items(next_url);
output.push(...response.results)
next_url = response.next;
}
} catch (e) {
// handle errors *here*, perhaps
console.log(e)
}
return output;
}
Note that .catch will result in a Promise being converted from a rejected Promise to a resolved one - you don't want to .catch everywhere, because that will make it difficult for the caller to detect errors.
Another way of doing it is to not use async at all and just recursively return a promise:
const getItems = (url) =>
axios({
baseURL: url,
method: 'get',
}).then((response) => response.data);
const getData = (initialUrl) => {
const recur = (result, nextUrl) =>
!nextUrl
? Promise.resolve(result)
: getItems(nextUrl).then((data) =>
recur(result.concat([data.results]), data.next),
);
return recur([],initialUrl)
.catch(e=>Promise.reject(e.stack));//reject with error stack
};
As CertainPerformance noted; you don't need to catch at every level, if you want getData to reject with error.stack you only need to catch it once.
However; if you had 100 next urls and 99 of them were fine but only the last one failed would you like to reject in a way that keeps the results so far so you can try again?
If you do then the code could look something like this:
const getData = (initialUrl) => {
const recur = (result, nextUrl) =>
!nextUrl
? Promise.resolve(result)
: getItems(nextUrl)
.catch(e=>Promise.reject([e,result]))//reject with error and result so far
.then((data) =>
recur(result.concat([data.results]), data.next),
);
return recur([],initialUrl);//do not catch here, just let it reject with error and result
};
Let's say I have a simple Node.js app that has these methods in another file:
module.exports = {
completeQuest(data) {
// Code here
},
killMonster(data) {
// Also code here
},
};
And they are not AJAX commands. These just manipulate some data within the app. I'd export as such in allActions.js:
const player = require('./playerActions');
module.exports = {
player,
};
and of course later, in main JS file const actions = require('./allActions');
So, generally, I'd do:
actions.player.killMonster();
actions.player.completeQuest();
But I want them to act one after the other. I know I can do async/await, but I'm not doing any AJAX calls, so would a Promise still be the best way?
What about using a yield function? Would that be good? I'm looking for opinions is all. Thank you.
I'm going to assume that killMonster and completeQuest perform asynchronous actions. (You've said they're "not ajax", but your question suggests they're not synchronous, either.)
Yes, this is a use case for promises, either explicit promises or those provided by async functions. Have killMonster and completeQuest return a promise (either by doing so explicitly or making them async functions), and then either:
actions.player.killMonster()
.then(
() => actions.player.completeQuest()
)
.catch(error => {
// Handle error
});
or, within an async function:
try {
await actions.player.killMonster();
await actions.player.completeQuest();
} catch (error) {
// Handle error
}
Here's a simple async/await example using setTimeout to provide the asynchronous part:
const delay = (ms, ...args) =>
new Promise(resolve => {
setTimeout(resolve, ms, ...args);
});
// Stand-ins for the actions
const player = {
actions: {
async killMonster() {
await delay(500, "monster killed");
},
async completeQuest() {
await delay(800, "quest complete");
}
}
};
// Top-leve async function (see my answer here:
// https://stackoverflow.com/questions/46515764/how-can-i-use-async-await-at-the-top-level
(async () => {
try {
console.log("Killing monster...");
await player.actions.killMonster();
console.log("Monster killed; completing question...");
await player.actions.killMonster();
console.log("Quest complete");
} catch (e) {
// Deal with error (probably don't just dump it to the console like this does)
console.error(e);
}
})();