Unhandled Promise Rejection in React - javascript

I have an app that read data from external devices. These data are like acceleration, gyroscopic, magnetometer and pressure.
I'm trying to read the data in this way:
async setupNotifications2(device) {
let i = 0
const service = this.serviceGeneral();
while(i<10 ) {
i++
const promises = [
device.readCharacteristicForService(service, this.AccGyrMg),
device.readCharacteristicForService(service, this.Pressure)
]
Promise.all(promises)
.then((values) => {
// const time = new Date().getMilliseconds()
const time = bufAccGyrMg.readInt16LE(0);
const [...acc_sx] = [0,2, 4, 6].map(index => bufAccGyrMg.readInt16LE(index));
this.setState(state => ({acc_sx,array_acc_sx: [...state.array_acc_sx,[time , acc_sx]]}));
const [...gyr_sx] = [8, 10, 12].map(index => bufAccGyrMg.readInt16LE(index));
this.setState(state => ({gyr_sx,array_gyr_sx: [...state.array_gyr_sx,[time , gyr_sx]]}));
const [...mg_sx] = [14,16,18].map(index => bufAccGyrMg.readInt16LE(index));
this.setState(state => ({gyr_sx,array_mg_sx: [...state.array_mg_sx,[time , mg_sx]]}));
const bufPressure = Buffer.from(values[1].value, "base64");
const [...pressure_sx] = [0, 2, 4, 6, 8].map(index => bufPressure.readUInt16LE(index));
this.setState(state => ({pressure_sx,array_pressure_sx: [...state.array_pressure_sx,[time, pressure_sx]]}));
})
}
}
Now I have insert a condition inside a while only to try the code.
When I start the app, I receive this error:
YellowBox.js:67 Possible Unhandled Promise Rejection (id: 0):
BleError: Operation was rejected
How can I do, in your opinion?? Thank you.

I refactored your code a bit to help get rid of the Unhandled Promise Rejection error and help you point down the issue:
async setupNotifications2(device) {
//first of all, put everything inside a big try/catch block
try {
let i = 0
const service = this.serviceGeneral();
while(i<10 ) {
i++
const promises = [
// then make sure every promise passed to Promise.all() catches its own errors
device.readCharacteristicForService(service, this.AccGyrMg).catch( e => console.log(`err in first readCharacteristicForService `, e)),
device.readCharacteristicForService(service, this.Pressure).catch( e => console.log(`err in second readCharacteristicForService `, e))
]
// giben you're in an async function, you can do this to simplify a bit your code:
const values = await Promise.all(promises);
// const time = new Date().getMilliseconds()
const time = bufAccGyrMg.readInt16LE(0);
// this is an array, no need to overcomplicate with destructuring assignment... you can do the same below
const acc_sx = [0,2, 4, 6].map(index => bufAccGyrMg.readInt16LE(index));
this.setState(state => ({acc_sx,array_acc_sx: [...state.array_acc_sx,[time , acc_sx]]}));
const [...gyr_sx] = [8, 10, 12].map(index => bufAccGyrMg.readInt16LE(index));
this.setState(state => ({gyr_sx,array_gyr_sx: [...state.array_gyr_sx,[time , gyr_sx]]}));
const [...mg_sx] = [14,16,18].map(index => bufAccGyrMg.readInt16LE(index));
this.setState(state => ({gyr_sx,array_mg_sx: [...state.array_mg_sx,[time , mg_sx]]}));
const bufPressure = Buffer.from(values[1].value, "base64");
const [...pressure_sx] = [0, 2, 4, 6, 8].map(index => bufPressure.readUInt16LE(index));
this.setState(state => ({pressure_sx,array_pressure_sx: [...state.array_pressure_sx,[time, pressure_sx]]}));
} catch (err){
console.error(`general error: `, err)
}
}

Related

Await doesn't doing the job

I got this map function on a array:
foldersTreeImagesFiles.map( async (paragliderFolder) => {
const pathfolder = pathImagesParaglider + '/' + paragliderFolder.subfolderName;
const imagesUrl = await paragliderFolder.ImagesParagilder.reduce( async (UrlsImage, image, index) => {
const pathImage = pathfolder + '/' + image;
const folderCoulidinary = 'paraglider/' + paragliderFolder.subfolderName;
const resu = await uploadImage(pathImage, folderCoulidinary);
UrlsImage.name = paragliderFolder.subfolderName
UrlsImage[`photo_${index}`] = resu;
return UrlsImage
}, {})
console.log(imagesUrl);
})
The array exemple :
[
{
subfolderName: 'Arcus_Rs',
ImagesParagilder: [
'swing_arcus2rs_slider_arcus6.jpg',
'swing_arcus2rs_slider_arcus7.jpg',
'swing_arcus2rs_slider_arcuslim4.jpg',
'swing_arcus2rs_slider_arcuslime9.jpg'
],
color: [
'swing_arcus2rs_flame.png',
'swing_arcus2rs_lime.png',
'swing_arcus2rs_nightshade.png',
'swing_arcus2rs_ocean.png'
]
},
{
subfolderName: 'Coden_Pro',
ImagesParagilder: [ 'DSC5495.jpg' ],
color: [ 'Air.png', 'Earth.png', 'Fire.png', 'Water.png' ]
},
{
subfolderName: 'Tonic_2',
ImagesParagilder: [
'DSC5349r.jpg',
'DSC6647r.jpg',
'P1044262r.jpg',
'P1044438r.jpg',
'P1044656r.jpg'
],
color: [ 'Lind.png', 'Mustard.png' ]
}
]
So i got this result :
{
name: 'Arcus_Rs',
photo_0: 'url********'
}
{
name: 'Coden_Pro',
photo_0: 'url********'
}
{
name: 'Tonic_2',
photo_0: 'url********'
}
i got only one photo, i should have more photo, so for me it the await of the reduce who doesn't work.
If i try const imagesUrl = await Promies.all(paragliderFolder.ImagesParagilder.reduce( ect...)
i have a error: TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))
i don't understand why the console log doesn't wait the end of the reduce.
When you do await in your reducer, the function immediately returns a Promise and quits. At the second iteration, UrlsImage is actually the Promise from the first iteration, not the object you put into the reducer. I think this is not what you want.
Try something like this:
const promises = paragliderFolder.ImagesParagilder.map( async (image, index) => {
const pathImage = pathfolder + '/' + image;
const folderCoulidinary = 'paraglider/' + paragliderFolder.subfolderName;
const resu = await uploadImage(pathImage, folderCoulidinary);
return [`photo_${index}`, resu]
})
const entries = await Promise.all(promises)
const imagesUrl = Object.fromEntries(entries)
This will load each image and give you a tuple from which you can build what I assume you want imagesUrl to look like. Not sure about the name property, it seems like you wanted to override it in every iteration.
Here is a dummy-application using it:
function uploadImage(pathImage, folderCoulidinary){
return new Promise((resolve) => {
resolve('uploaded to:' + pathImage)
})
}
const paragliderFolder = {
ImagesParagilder: [
'img1', 'img2'
]
}
const pathfolder = 'pathfolder'
async function runit(){
const promises = paragliderFolder.ImagesParagilder.map( async (image, index) => {
const pathImage = pathfolder + '/' + image;
const folderCoulidinary = 'paraglider/' + paragliderFolder.subfolderName;
const resu = await uploadImage(pathImage, folderCoulidinary);
return [`photo_${index}`, resu]
})
const entries = await Promise.all(promises)
return Object.fromEntries(entries)
}
runit().then(o => console.log(o))
Hello after many research and trials here what i find.
the array.reduce() send only one promise so we can't use the Promise.all().
Here the trick to use a reduce with array of promise:
const array = [4, 7, 78];
const postsPromise = array.reduce((acc, element, index) => {
return acc.then(async result => {
const post = await fetch(`https://jsonplaceholder.typicode.com/posts/${element}`).then(res => res.json());
return { ...result, [`Id_${index}`]: post.title };
});
}, Promise.resolve({}));
const posts = await postsPromise;
console.log(posts);
The Promise.resolve send a Promise already resolved and we loop over it

It is not possible to make a loop through the array

There is a request to Firebase, using then I add new elements to the array. If you output console.log, then there are elements, but lenght = 0 and loops do not work.
export const useLastMessageDialogs = (messagesStatus: StatusDialogType): UserMessageType[] => {
const messages: string[] = [];
useEffect(() => {
const querySideDialogs = query(
collection(firestoreDb, 'questions'),
where('status', '==', messagesStatus)
);
onSnapshot(querySideDialogs, (dialogs) => {
dialogs.docChanges().forEach((dialog) => {
getDocs(
query(
collection(firestoreDb, 'questions', dialog.doc.id, 'messages'),
orderBy('timestamp', 'desc'),
limit(1)
)
).then((response) => {
response.forEach((message) => {
messages.push('bebebe');
});
});
});
});
}, [messagesStatus]);
console.log(messages); // [0: "bebebe", 1: "bebebe" length: 2]
console.log(messages.length); // 0
return [];
};
I took it to a separate service and connected it via redux-saga

JavaScript PromiseAll allSettled does not catch the rejected

I have a sns lambda function that returns void (https://docs.aws.amazon.com/lambda/latest/dg/with-sns.html). This event orderId and one message'status: success' are what I'm publishing. I check if the 'orderId' exists in my data database in the sns subscription lambda event. If it already exists, update the database; if it doesn't, console error it.
I created an integration test in which I transmit a random 'uuid' that isn't a valid 'orderId,' but it appears that my promise doesn't capture the'rejected'. It should show in console error failed to find order... I'm not sure where I'm going wrong. Also My promise syntax looks complicated, is there any neat way, I can do it. Thank you in advance 🙏🏽
This is sns event, which listen the publishing
interface PromiseFulfilledResult<T> {
status: "fulfilled" | "rejected";
value: T;
}
const parseOrdersFromSns = (event: SNSEvent) => {
try {
return event.Records.flatMap((r) => JSON.parse(r.Sns.Message))
} catch (error) {
console.error('New order from SNS failed at parsing orders', { event }, error)
return []
}
}
export const handlerFn = async (event: SNSEvent): Promise<void> => {
const orders = parseOrdersFromSns(event)
if (orders.length === 0) return
const existingOrdersPromiseResult = await Promise.allSettled(
orders.map(
async (o) => await findOrderStateNode(tagOrderStateId(o.orderId))
)
); // This returns of data if the order exsiit other it will return undefined
const existingOrders = existingOrdersPromiseResult // should returns arrays of data
.filter(({ status }) => status === "fulfilled")
.map(
(o) =>
(
o as PromiseFulfilledResult<
TaggedDatabaseDocument<
OrderStateNode,
TaggedOrderStateId,
TaggedOrderStateId
>
>
).value
);
const failedOrders = existingOrdersPromiseResult.filter( // should stop the opeartion if the data is exsit
({ status }) => status === "rejected"
);
failedOrders.forEach((failure) => {
console.error("failed to find order", { failure });
});
const updateOrder = await Promise.all(
existingOrders.map((o) => {
const existingOrderId = o?.pk as TaggedOrderStateId;
console.log({ existingOrderId }); // Return Undefined
})
);
return updateOrder;
};
this is my test suite
describe('Creating and updating order', () => {
integrationTest(
'Creating and updating the order',
async (correlationId: string) => {
CorrelationIds.set('x-correlation-id', correlationId)
const createdOrder = await createNewOrder(correlationId) // This create random order
if (!createdOrder.id) {
fail('order id is not defined')
}
const order = await getOrder(createdOrder.id)
// Add new order to table
await initializeOrderState([order])
const exisitingOrder = await findOrderStateNode(tagOrderStateId(order.id))
if (!exisitingOrder) fail(`Could not existing order with this orderId: ${order.id}`)
const event = {
Records: [
{
Sns: {
Message: JSON.stringify([
{
orderId: uuid(), // random order it
roundName,
startTime,
},
{
orderId: order.id,
roundName,
startTime,
},
{
orderId: uuid(),
roundName,
startTime,
},
]),
},
},
],
} as SNSEvent
await SnsLambda(event)
const updateOrderState = await findOrderStateNode(tagOrderStateId(order.id))
expect(updateOrderState?.status).toEqual('success')
},
)
})

Run Promise all in an await for loop

I have a nested array of Promise function. Eg:
let callSet = [
[ func1 , func2 , func3],
[ func4 , func5 , func6],
[ func7 , func8 , func9],
]
the response of await func1() will be in below structure:
{
data : [ {id: 1} , {id:2}],
status: 400
}
I want to run it in a for loop so that they run in batch sequentially and incrementally load the data into array as they come in. Tried below code but I am lost on how to do it:
const finalData = [];
private asyncForEach(PromiseGroups): Promise<any> {
return PromiseGroups.reduce(async (acc, cItem) => {
const results = await acc;
const res = await Promise.all(cItem) as any;
finalData = [...finalData , ...[].concat(...res.map(r => r.data))];
return results
}, Promise.resolve([]))
}
I would like to load it in as:
[ {id: 1}, {id:2} , {id: 3} ..... ]
and this should get updated as the Promise all is getting resolved
I want to wait till func1 , func2 , func3 is resolved and then move to func4 , func5 , func6 . and once I get data of func4 , func5 , func6 , I want to push it with the data of func1 , func2 , func3
Try the below code:
private init(){
let callSet = [
[ func1 , func2 , func3],
[ func4 , func5 , func6],
[ func7 , func8 , func9],
];
this.asyncForEach(callSet,this.fetchInBatch.bind(this) )
}
private asyncForEach(funcGrpList, execFunc) {
return funcGrpList.reduce((p,funcList) => {
return p.then(() => execFunc(funcList));
}, Promise.resolve());
}
private fetchInBatch(pageGroupList) {
return Promise.all(pageGroupList).then((res: any) => {
this.finalData = [...this.finalData , ...[].concat(...res.map(r => r.data))];
})
}
This should work as expected
Edit: Assuming the last array result is return directly because there is no need to wait for next loop to be finished.
async function run(callSet) {
const output = [];
let prev = [];
const len = callSet.length;
for (let i = 0; i < len; i++) {
const array = await Promise.all(callSet[i].map(func => func()));
const data = array.map(item => item.data);
if (i === 0) {
// no need to append item to output
// just append item to previous array for next loop to use.
prev.push(...data);
} else if (i < len) {
// append item to output from previous result.
output.push(...prev);
prev = [];
// append data to previous result for next loop.
prev.push(...data);
} else {
//last loop, just append data from previous result and current result
output.push(...prev);
output.push(...data);
}
}
console.log(output);
}
This will call the execution sets in the requested order and timing and add the return data as soon as the group of promises returns
const finalData = [];
async function execute() {
for (const set of callSet) {
const resp = await Promise.all(set.map(f => f()));
finalData.push(...resp.map(r => r.data).flat());
}
}
Once execute() is called finalData will be updated asynchronously once for each 'row' of functions.
For future readers
resp.map(r => r.data).flat() is due to the specified promises payload. If someone needs just to pack the results together the code would be:
for (const set of callSet) {
const resp = await Promise.all(set);
finalData.push(...resp);
}
If you want to load the data in chunks but produce flat array of results, your easiest option is to use async/await syntax:
interface Data {
id: number
}
interface DataResponse {
data: Data[];
status: number;
}
type AsyncCall = () => Promise<DataResponse>;
/* ... */
const result: Data[] = [];
for(const chunk of callSet) {
const chunkResult = await Promise.all(chunk.map(f => f()));
result.push(...chunkResult.flatMap(x => x.data));
}
Playground Link
JavaScript demo:
/* mock section */
const fakeFunc = (id1, id2) => ()=>
Promise.resolve({
data : [{id: id1} , {id: id2}],
status: 400
});
const func1 = fakeFunc(1, 2),
func2 = fakeFunc(3, 4),
func3 = fakeFunc(5, 6),
func4 = fakeFunc(7, 8),
func5 = fakeFunc(9, 10),
func6 = fakeFunc(11, 12),
func7 = fakeFunc(13, 14),
func8 = fakeFunc(15, 16),
func9 = fakeFunc(17, 18)
;
/* /mock section */
async function main() {
let callSet = [
[ func1 , func2 , func3],
[ func4 , func5 , func6],
[ func7 , func8 , func9],
];
const result = [];
for(const chunk of callSet) {
const chunkResult = await Promise.all(chunk.map(f => f()));
result.push(...chunkResult.flatMap(x => x.data));
}
return result;
}
main()
.then(r => console.log(r));
I you prefer to use the Promise API only, instead of async/await, then you can reduce into a promise like this:
interface Data {
id: number
}
interface DataResponse {
data: Data[];
status: number;
}
type AsyncCall = () => Promise<DataResponse>;
/* ... */
const result = callSet.reduce((p: Promise<Data[]>, chunk: AsyncCall[]) =>
p.then(acc =>
Promise.all(chunk.map(f => f()))
.then(chunkResult => acc.concat(chunkResult.flatMap(x => x.data))))
, Promise.resolve([]));
Playground Link
JavaScript demo:
/* mock section */
const fakeFunc = (id1, id2) => () =>
Promise.resolve({
data : [{id: id1} , {id: id2}],
status: 400
});
const func1 = fakeFunc(1, 2),
func2 = fakeFunc(3, 4),
func3 = fakeFunc(5, 6),
func4 = fakeFunc(7, 8),
func5 = fakeFunc(9, 10),
func6 = fakeFunc(11, 12),
func7 = fakeFunc(13, 14),
func8 = fakeFunc(15, 16),
func9 = fakeFunc(17, 18)
;
/* /mock section */
function main() {
let callSet = [
[ func1 , func2 , func3],
[ func4 , func5 , func6],
[ func7 , func8 , func9],
];
const result = callSet.reduce((p, chunk) =>
p.then(acc =>
Promise.all(chunk.map(f => f()))
.then(chunkResult => acc.concat(chunkResult.flatMap(x => x.data))))
, Promise.resolve([]));
return result;
}
main()
.then(r => console.log(r));
However, it is a bit ugly. It can be improved by extracting some functions:
interface Data {
id: number
}
interface DataResponse {
data: Data[];
status: number;
}
type AsyncCall = () => Promise<DataResponse>;
/* ... */
const combineWith = (acc: Data[]) => (chunkResult: DataResponse[]) =>
acc.concat(chunkResult.flatMap(x => x.data));
const process = (chunk: AsyncCall[]) => (acc: Data[]) =>
Promise.all(chunk.map(f => f()))
.then(combineWith(acc));
/* ... */
const result = callSet.reduce((p: Promise<Data[]>, chunk: AsyncCall[]) =>
p.then(process(chunk))
, Promise.resolve([]))
Playground Link
JavaScript demo:
/* mock section */
const fakeFunc = (id1, id2) => () =>
Promise.resolve({
data : [{id: id1} , {id: id2}],
status: 400
});
const func1 = fakeFunc(1, 2),
func2 = fakeFunc(3, 4),
func3 = fakeFunc(5, 6),
func4 = fakeFunc(7, 8),
func5 = fakeFunc(9, 10),
func6 = fakeFunc(11, 12),
func7 = fakeFunc(13, 14),
func8 = fakeFunc(15, 16),
func9 = fakeFunc(17, 18)
;
/* /mock section */
const combineWith = (acc) => (chunkResult) =>
acc.concat(chunkResult.flatMap(x => x.data));
const process = (chunk) => (acc) =>
Promise.all(chunk.map(f => f()))
.then(combineWith(acc));
function main() {
let callSet = [
[ func1 , func2 , func3],
[ func4 , func5 , func6],
[ func7 , func8 , func9],
];
const result = callSet.reduce((p, chunk) =>
p.then(process(chunk))
, Promise.resolve([]));
return result;
}
main()
.then(r => console.log(r));

Create a time counter to insert in array

I have a problem with creating a time in milliseconds to add to an array.
I have an app that reads from two external devices. So I need to insert a time about when these data are read.
I have a function like this:
async setupNotifications1(device) {
const service = this.serviceGeneral();
device.monitorCharacteristicForService(
service,
this.AccGyrMg,
(error, characteristic) => {
if (error) {
this.error(error.message);
return;
}
const buf = Buffer.from(characteristic.value, "base64");
const [...acc_dx] = [2, 4, 6].map(index => buf.readInt16LE(index));
this.setState(state => ({
acc_dx,
array_acc_dx: [
...state.array_acc_dx,
[acc_dx],
]
}));
/* pressure */
device.monitorCharacteristicForService(
service,
this.Pressure,
(error, characteristic) => {
if (error) {
this.error(error.message);
return;
}
const buf = Buffer.from(characteristic.value, "base64");
const [...pressure_dx] = [0, 2, 4, 6, 8].map(index => buf.readUInt16LE(index));
this.setState(state => ({
pressure_dx,
array_pressure_dx: [
...state.array_pressure_dx,
[pressure_dx]
]
}));
}
);
How can I create a time to concatenate at [...acc_dx] and [pressure_dx] ? Thank you
You could look into using setTimeout() for doing this.
https://facebook.github.io/react-native/docs/timers
So in your case you could have something like:
setTimeout(() => {
addToArray();
}, 300);

Categories

Resources