Reference error, says variable is not defined - javascript

So I have a function which reads from Firebase database and then creates an array of objects. I have defined variable key, but it is still unavailable.
var usersList = [];
const ref = firebase.database().ref()
function fetchUsers() {
ref.child('users').once('value').then(snap => {
var promises = [];
snap.forEach(childSnap => {
var key = childSnap.key
promises.push
(ref.child(`users/${key}/points`).once('value')
);
});
return Promise.all(promises);
}).then(function(snapshots) {
return snapshots.map(snapper => {
var points = snapper.val()
return {uid: key, points: points};
})
}).then(function(usersList) {
console.log(usersList)
})
}
And this is the error I get...
(node:11724) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): ReferenceError: key is not defined
If i just do: key = childSnap.key, then every object's uid is the same.

key is only defined within the forEach callback. Naturally you cannot then reference it from within the second then callback. Moreover, which key would it be? For the first entry? The second? Third?
Instead, you need to return the key with the value:
function fetchUsers() {
ref.child('users').once('value').then(snap => {
var promises = [];
snap.forEach(childSnap => {
var key = childSnap.key;
promises.push(
ref.child(`users/${key}/points`).once('value').then(
snapper => ({key, snapper}) // **
)
);
});
return Promise.all(promises);
}).then(function(snapshots) {
return snapshots.map(({key, snapper}) => { // **
var points = snapper.val();
return {uid: key, points: points};
});
}).then(function(usersList) {
console.log(usersList);
});
}
On the first ** line above, we're transforming the result from once so it returns an object with both the key and "snapper".
On the second ** line, we're using destructured parameters to receive those objects as the distinct key and snapper parameters.
FWIW, if you want to aggressively use concise arrows:
function fetchUsers() {
ref.child('users').once('value').then(snap =>
Promise.all(snap.map(childSnap => {
const key = childSnap.key;
return ref.child(`users/${key}/points`)
.once('value')
.then(snapper => ({key, snapper}));
}))
).then(snapshots =>
snapshots.map(({key, snapper}) => ({uid: key, points: snapper.val()}))
).then(usersList => {
console.log(usersList);
});
}
Also note the use of map rather than declaring an array and pushing to it from a forEach callback. :-)

Related

Reactjs array mutation

I have this function:
setNotActiveWalletsList = () => {
const { GetAccounts } = this.props;
let shallowCopyOfWalletsArray = [...GetAccounts]
const notActive = shallowCopyOfWalletsArray.filter(user => user.active !== true);
let newArr = notActive.map(item => {
return decryptAccountInformation(item).then(result => {
!result.address ? null : item.address = result.address
})
});
this.setState({ onlyNotActive: newArr });
}
GetAccounts is an array of objects
The issue is, One of my colleagues have told me that I am mutating the array with this line:
!result.address ? null : item.address = result.address
But I don't really understand why is this considered a mutation? I am sure I created a copy of the original array and modified it.
Any suggestions on how to resolve this, please?
Spread syntax just does a one level closing of the object or array. Any object or array which is more than one level deep will still have the same reference. Hence when you using notActive array items, you are essentially working on the same reference that was inside GetAccounts
The correct way to update is to return the cloned and updated reference from within the map function and using Promise.all to also handle the async call
setNotActiveWalletsList = () => {
const { GetAccounts } = this.props;
let shallowCopyOfWalletsArray = [...GetAccounts]
const notActive = shallowCopyOfWalletsArray.filter(user => user.active !== true);
let promises = notActive.map(item => {
return decryptAccountInformation(item).then(result => {
return !result.address ? item : {...item, address: result.address}
})
});
Promise.all(promises).then(newArr => this.setState({ onlyNotActive: newArr }));
}

Param didn't pass to local function in firebase cloud functions

I have a firebase cloud function as follows:
exports.foo = functions.database
.ref("/candidates/{jobTrack}/{candidateId}")
.onCreate((snap, context) => {
const candidate = snap.val().candidate;
const jobTrack = context.params.jobTrack;
const jobsRef = admin.database().ref("jobs");
return jobsRef
.child(jobTrack)
.once("value")
.then(jobs => {
const promises = [];
jobs.forEach(job => {
promises.push(job.val());
});
return Promise.all(promises);
})
.then(jobs => {
return jobs.forEach(job => {
var percent = getMatchedPercent(candidate, job);
if (percent >= 0.9) {
admin
.database()
.ref("feeds")
.child(job.feedId)
.child("upcomingWeek")
.push(candidate); // add to team's feed
}
});
})
.catch(err => {
console.log("firebase got an error: ", err);
});
});
In function foo, I call a local non-cloud function getMatchedPercent which is defined as below:
const getMatchedPercent = (candidate, job) => {
console.log("In get percent: ", candidate, job);
// do something
};
The problem is when I checked job.val() in foo before calling getMatchedPercent, I can see valid data got printed from console for job.val(). When once get in getMatchedPercent, I tried to print job, it complains it's undefined.
Is there anything I missed? Why the information of job can be lost during calling a function? Thanks!
Your problem is caused by these lines:
const promises = [];
jobs.forEach(job => {
promises.push(job.val());
});
return Promise.all(promises);
job.val() returns an object (of the data) not a promise, so Promise.all() incorrectly interprets it as a resolved promise with no value. In your next block of code, the array jobs is an array of undefined values rather than the data you were expecting.
To fix this, you would instead return the array of values rather than using Promise.all().
const jobValues = [];
jobs.forEach(job => {
jobValues.push(job.val());
});
return jobValues;
But because no asyncronous work is taking place here you can flatten your Promise chain. By doing so, you will use less memory because you won't need an array containing of all of your job.val() objects at once.
exports.foo = functions.database
.ref("/candidates/{jobTrack}/{candidateId}")
.onCreate((snap, context) => {
const candidate = snap.val().candidate;
const jobTrack = context.params.jobTrack;
const jobsRef = admin.database().ref("jobs");
return jobsRef
.child(jobTrack)
.once("value")
.then(jobs => {
const promises = []; // will contain any team feed update promises
jobs.forEach(jobSnapshot => { // This is DataSnapshot#forEach
const job = jobSnapshot.val();
const percent = getMatchedPercent(candidate, job);
if (percent >= 0.9) {
promises.push(
admin
.database()
.ref("feeds")
.child(job.feedId)
.child("upcomingWeek")
.push(candidate) // add to team's feed
);
}
});
return Promise.all(promises);
})
.catch(err => {
console.log("Failed to update team feeds: ", err);
});
});
However, this still has another problem where some of the feed updates may succeed and others may fail which leaves your database in an unknown state. So instead you might want to consider writing to the database atomically (all data is written, or nothing at all).
This could be achieved using:
exports.foo = functions.database
.ref("/candidates/{jobTrack}/{candidateId}")
.onCreate((snap, context) => {
const candidate = snap.val().candidate;
const jobTrack = context.params.jobTrack;
const jobsRef = admin.database().ref("jobs");
return jobsRef
.child(jobTrack)
.once("value")
.then(jobs => {
const pendingUpdates = {}; // "path: value" pairs to be applied to the database
const feedsRef = admin.database().ref("feeds");
jobs.forEach(jobSnapshot => { // This is DataSnapshot#forEach
const job = jobSnapshot.val();
const percent = getMatchedPercent(candidate, job);
if (percent >= 0.9) {
const pushId = feedsRef.push().key; // push() without arguments doesn't write anything to the database, it just generates a new reference with a push ID we can use.
const path = job.feedId + "/upcomingWeek/" + pushId;
pendingUpdates[path] = candidate; // queue add to team's feed
}
});
// apply all updates in pendingUpdates object,
// relative to feedsRef as an all-or-nothing operation.
// e.g. pendingUpdates["feed001/upcomingWeek/9jksdfghsdjhn"] = "someUserId"
// will be written to "feeds/feed001/upcomingWeek/9jksdfghsdjhn"
return feedsRef.update(pendingUpdates); // commit changes
})
.catch(err => {
console.log("Failed to apply all feed updates: ", err);
});
});

Array from recursive promises returns undefined

I am using Jake Archibald's indexedDB promise wrapper.
I have an object store that contains json objects and a separate auto incremented key. When I retrieve the objects i need to also get the key so i can later delete them.
I'm using iterateCursor to recursively step through the cursor so that i can directly add the key and value to an array which i return as a resolved promise.
static getThings(){
return thingsDb.then(db => {
let keyVals = [];
const tx = db.transaction('things');
const unsyncedStore = tx.objectStore('things');
return unsyncedStore.iterateCursor(cursor => {
if(!cursor) return Promise.resolve(keyVals);
console.log(`key: ${cursor.primaryKey}, val: ${cursor.value}`);
keyVals.push({key: cursor.primaryKey, value: cursor.value});
cursor.continue();
});
}).catch(error => {
console.error(error);
});
}
however when I call
DBHelper.getThings().then(returnedArray=> {
// do something with returnedArray
})
it throws an error saying the returned array is undefined.
iterateCursor doesn't return anything (i.e. returns undefined)
You need to return the promise available at unsyncedStore.complete
But this promise won't resolve to a useful value, so, use .then to return the keyVals
Also, no point in doing if(!cursor) return Promise.resolve(keyVals); since the return value from the iterateCursor callback is ignored
static getThings() {
return thingsDb.then(db => {
let keyVals = [];
const tx = db.transaction('things');
const unsyncedStore = tx.objectStore('things');
// no return here since iterateCursor doesn't return anything useful anyway
unsyncedStore.iterateCursor(cursor => {
if (!cursor) return;
console.log(`key: ${cursor.primaryKey}, val: ${cursor.value}`);
keyVals.push({key: cursor.primaryKey, value: cursor.value});
cursor.continue();
});
// return here - complete is a promise that resolves when the iterateCursor completes
return unsyncedStore.complete.then(() => keyVals);
}).catch(error => {
console.error(error);
});
}

Recursive Promise.all with a snapshot in firebase

I have the following structure on my firebase database:
I need to get the values of the keys pin. For that I'm working with a recursive function like this:
let pins = [];
const normalize = (snapchot) => {
snapchot.forEach(function(child) {
if(child.val().pin) {
pins.push(Promise.resolve(child.val().pin));
}
else normalize(child);
});
return Promise.all(pins);
}
And now, call the normalize function:
normalize(snapshot) // snapshot represents the data from the firebase db
.then(p => {
console.log(p); // [ 'mi-pin-agosto', 'mi-pin-julio' ]
})
.catch(err => {
// handle error
})
And it works, but when I debug that code, I see that return Promise.all(pins); gets called more than one time. I only need to be called only once, after the foreach have been completly finished; that's with the idea for the case of performance, because the snapshot data it's more large than the see it in the image I show.
Any ideas ???
to only use Promise.all once you can have the recursive function as a function "inside" `normalize
const normalize = (snapshot) => {
const process = x => {
let ret = [];
x.forEach(function(child) {
if(child.val().pin) {
ret.push(Promise.resolve(child.val().pin));
} else {
ret = ret.concat(process(child));
}
});
return ret;
});
return Promise.all(process(snapshot));
}
This code also doesn't require a global array to store the results
However, as there is nothing asynchronous about any of the code you are calling - dispense with the Promises inside normalize
const normalize = (snapshot) => {
let ret = [];
snapshot.forEach(function(child) {
if(child.val().pin) {
ret.push(child.val().pin);
} else {
ret = ret.concat(normalize(child));
}
});
return ret;
};
If you really have to use Promises for this code, you can simply
Promise.all(normalize(snapshot))
.then(p => {
console.log(p); // [ 'mi-pin-agosto', 'mi-pin-julio' ]
})
.catch(err => {
// handle error
})

How to use Promise.all with an object as input

I've been working on a small 2D game library for my own use, and I've run into a bit of a problem. There is a particular function in the library called loadGame that takes dependency info as input (resource files, and a list of scripts ot be executed). Here's an example.
loadGame({
"root" : "/source/folder/for/game/",
"resources" : {
"soundEffect" : "audio/sound.mp3",
"someImage" : "images/something.png",
"someJSON" : "json/map.json"
},
"scripts" : [
"js/helperScript.js",
"js/mainScript.js"
]
})
Each item in resources has a key that is used by the game to access that particular resource. The loadGame function converts the resources into an object of promises.
The problem is that it tries to use Promises.all to check for when they're all ready, but Promise.all accepts only iterables as inputs - so an object like what I have is out of the question.
So I tried to convert the object into an array, this works great, except each resource is just an element in an array and doesn't have a key to identify them.
Here's the code for loadGame:
var loadGame = function (game) {
return new Promise(function (fulfill, reject) {
// the root folder for the game
var root = game.root || '';
// these are the types of files that can be loaded
// getImage, getAudio, and getJSON are defined elsewhere in my code - they return promises
var types = {
jpg : getImage,
png : getImage,
bmp : getImage,
mp3 : getAudio,
ogg : getAudio,
wav : getAudio,
json : getJSON
};
// the object of promises is created using a mapObject function I made
var resources = mapObject(game.resources, function (path) {
// get file extension for the item
var extension = path.match(/(?:\.([^.]+))?$/)[1];
// find the correct 'getter' from types
var get = types[extension];
// get it if that particular getter exists, otherwise, fail
return get ? get(root + path) :
reject(Error('Unknown resource type "' + extension + '".'));
});
// load scripts when they're done
// this is the problem here
// my 'values' function converts the object into an array
// but now they are nameless and can't be properly accessed anymore
Promise.all(values(resources)).then(function (resources) {
// sequentially load scripts
// maybe someday I'll use a generator for this
var load = function (i) {
// load script
getScript(root + game.scripts[i]).then(function () {
// load the next script if there is one
i++;
if (i < game.scripts.length) {
load(i);
} else {
// all done, fulfill the promise that loadGame returned
// this is giving an array back, but it should be returning an object full of resources
fulfill(resources);
}
});
};
// load the first script
load(0);
});
});
};
Ideally I'd like for some way to properly manage a list of promises for resources while still mantaining an identifier for each item. Any help would be appreciated, thanks.
First of all: Scrap that Promise constructor, this usage is an antipattern!
Now, to your actual problem: As you have correctly identified, you are missing the key for each value. You will need to pass it inside each promise, so that you can reconstruct the object after having awaited all items:
function mapObjectToArray(obj, cb) {
var res = [];
for (var key in obj)
res.push(cb(obj[key], key));
return res;
}
return Promise.all(mapObjectToArray(input, function(arg, key) {
return getPromiseFor(arg, key).then(function(value) {
return {key: key, value: value};
});
}).then(function(arr) {
var obj = {};
for (var i=0; i<arr.length; i++)
obj[arr[i].key] = arr[i].value;
return obj;
});
Mightier libraries such as Bluebird will also provide this as a helper function, like Promise.props.
Also, you shouldn't use that pseudo-recursive load function. You can simply chain promises together:
….then(function (resources) {
return game.scripts.reduce(function(queue, script) {
return queue.then(function() {
return getScript(root + script);
});
}, Promise.resolve()).then(function() {
return resources;
});
});
If you use lodash library, you can achieve this by a one-liner function:
Promise.allValues = async (object) => {
return _.zipObject(_.keys(object), await Promise.all(_.values(object)))
}
I actually created a library just for that and published it to github and npm:
https://github.com/marcelowa/promise-all-properties
https://www.npmjs.com/package/promise-all-properties
The only thing is that you will need to assign a property name for each promise in the object...
here's an example from the README
import promiseAllProperties from 'promise-all-properties';
const promisesObject = {
someProperty: Promise.resolve('resolve value'),
anotherProperty: Promise.resolve('another resolved value'),
};
const promise = promiseAllProperties(promisesObject);
promise.then((resolvedObject) => {
console.log(resolvedObject);
// {
// someProperty: 'resolve value',
// anotherProperty: 'another resolved value'
// }
});
Here is a simple ES2015 function that takes an object with properties that might be promises and returns a promise of that object with resolved properties.
function promisedProperties(object) {
let promisedProperties = [];
const objectKeys = Object.keys(object);
objectKeys.forEach((key) => promisedProperties.push(object[key]));
return Promise.all(promisedProperties)
.then((resolvedValues) => {
return resolvedValues.reduce((resolvedObject, property, index) => {
resolvedObject[objectKeys[index]] = property;
return resolvedObject;
}, object);
});
}
Usage:
promisedProperties({a:1, b:Promise.resolve(2)}).then(r => console.log(r))
//logs Object {a: 1, b: 2}
class User {
constructor() {
this.name = 'James Holden';
this.ship = Promise.resolve('Rocinante');
}
}
promisedProperties(new User).then(r => console.log(r))
//logs User {name: "James Holden", ship: "Rocinante"}
Note that #Bergi's answer will return a new object, not mutate the original object. If you do want a new object, just change the initializer value that is passed into the reduce function to {}
Here is #Matt's answer, with some types and some renames, and using ECMA-2019 Object.fromEntries.
// delayName :: (k, Promise a) -> Promise (k, a)
const delayName = ([name, promise]) => promise.then((result) => [name, result]);
export type PromiseValues<TO> = {
[TK in keyof TO]: Promise<TO[TK]>;
};
// promiseObjectAll :: {k: Promise a} -> Promise {k: a}
export const promiseObjectAll = <T>(object: PromiseValues<T>): Promise<T> => {
const promiseList = Object.entries(object).map(delayName);
return Promise.all(promiseList).then(Object.fromEntries);
};
function resolveObject(obj) {
return Promise.all(
Object.entries(obj).map(async ([k, v]) => [k, await v])
).then(Object.fromEntries);
}
credit to Cyril Auburtin for that genius at https://esdiscuss.org/topic/modify-promise-all-to-accept-an-object-as-a-parameter
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function test() {
console.time(1);
console.log(await resolveObject({
foo: delay(5).then(()=>1),
bar: delay(120).then(()=>2)
}));
console.timeEnd(1);
}
Using async/await and lodash:
// If resources are filenames
const loadedResources = _.zipObject(_.keys(resources), await Promise.all(_.map(resources, filename => {
return promiseFs.readFile(BASE_DIR + '/' + filename);
})))
// If resources are promises
const loadedResources = _.zipObject(_.keys(resources), await Promise.all(_.values(resources)));
Based off the accepted answer here, I thought I'd offer a slightly different approach that seems easier to follow:
// Promise.all() for objects
Object.defineProperty(Promise, 'allKeys', {
configurable: true,
writable: true,
value: async function allKeys(object) {
const resolved = {}
const promises = Object
.entries(object)
.map(async ([key, promise]) =>
resolved[key] = await promise
)
await Promise.all(promises)
return resolved
}
})
// usage
Promise.allKeys({
a: Promise.resolve(1),
b: 2,
c: Promise.resolve({})
}).then(results => {
console.log(results)
})
Promise.allKeys({
bad: Promise.reject('bad error'),
good: 'good result'
}).then(results => {
console.log('never invoked')
}).catch(error => {
console.log(error)
})
Usage:
try {
const obj = await Promise.allKeys({
users: models.User.find({ rep: { $gt: 100 } }).limit(100).exec(),
restrictions: models.Rule.find({ passingRep: true }).exec()
})
console.log(obj.restrictions.length)
} catch (error) {
console.log(error)
}
I looked up Promise.allKeys() to see if someone had already implemented this after writing this answer, and apparently this npm package does have an implementation for it, so use that if you like this little extension.
I've written function that recursively awaits promises within an object and returns the constructed object back to you.
/**
* function for mimicking async action
*/
function load(value) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(value);
}, Math.random() * 1000);
});
}
/**
* Recursively iterates over object properties and awaits all promises.
*/
async function fetch(obj) {
if (obj instanceof Promise) {
obj = await obj;
return fetch(obj);
} else if (Array.isArray(obj)) {
return await Promise.all(obj.map((item) => fetch(item)));
} else if (obj.constructor === Object) {
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
obj[key] = await fetch(obj[key]);
}
return obj;
} else {
return obj;
}
}
// now lets load a world object which consists of a bunch of promises nested in each other
let worldPromise = {
level: load('world-01'),
startingPoint: {
x: load('0'),
y: load('0'),
},
checkpoints: [
{
x: load('10'),
y: load('20'),
}
],
achievments: load([
load('achievement 1'),
load('achievement 2'),
load('achievement 3'),
]),
mainCharacter: {
name: "Artas",
gear: {
helmet: load({
material: load('steel'),
level: load(10),
}),
chestplate: load({
material: load('steel'),
level: load(20),
}),
boots: load({
material: load('steel'),
level: load(20),
buff: load('speed'),
}),
}
}
};
//this will result an object like this
/*
{
level: Promise { <pending> },
startingPoint: {
x: Promise { <pending> },
y: Promise { <pending> }
},
checkpoints: [ { x: [Promise], y: [Promise] } ],
achievments: Promise { <pending> },
mainCharacter: {
name: 'Artas',
gear: {
helmet: [Promise],
chestplate: [Promise],
boots: [Promise]
}
}
}
*/
//Now by calling fetch function, all promise values will be populated
//And you can see that computation time is ~1000ms which means that all processes are being computed in parallel.
(async () => {
console.time('start');
console.log(worldPromise);
let world = await fetch(worldPromise);
console.log(world);
console.timeEnd('start');
})();
Missing Promise.obj() method
A shorter solution with vanilla JavaScript, no libraries, no loops, no mutation
Here is a shorter solution than other answers, using modern JavaScript syntax.
The middle line process = ... is recursive and handles deep objects.
This creates a missing Promise.obj() method that works like Promise.all() but for objects:
const asArray = obj => [].concat(...Object.entries(obj));
const process = ([key, val, ...rest], aggregated = {}) =>
rest.length ?
process(rest, {...aggregated, [key]: val}) :
{...aggregated, [key]: val};
const promisedAttributes = obj => Promise.all(asArray(obj)).then(process);
// Promise.obj = promisedAttributes;
Better not use the last line! A much better idea is that you export this promisedAttributes as a utility function that you reuse.
Edit: This question seems to be gaining a little traction lately, so I thought I'd add my current solution to this problem which I'm using in a couple projects now. It's a lot better than the code at the bottom of this answer which I wrote two years ago.
The new loadAll function assume its input is an object mapping asset names to promises, and it also makes use of the experimental function Object.entries, which may not be available in all environments.
// fromEntries :: [[a, b]] -> {a: b}
// Does the reverse of Object.entries.
const fromEntries = list => {
const result = {};
for (let [key, value] of list) {
result[key] = value;
}
return result;
};
// addAsset :: (k, Promise a) -> Promise (k, a)
const addAsset = ([name, assetPromise]) =>
assetPromise.then(asset => [name, asset]);
// loadAll :: {k: Promise a} -> Promise {k: a}
const loadAll = assets =>
Promise.all(Object.entries(assets).map(addAsset)).then(fromEntries);
So I've come up with the proper code based on Bergi's answer. Here it is if anyone else is having the same problem.
// maps an object and returns an array
var mapObjectToArray = function (obj, action) {
var res = [];
for (var key in obj) res.push(action(obj[key], key));
return res;
};
// converts arrays back to objects
var backToObject = function (array) {
var object = {};
for (var i = 0; i < array.length; i ++) {
object[array[i].name] = array[i].val;
}
return object;
};
// the actual load function
var load = function (game) {
return new Promise(function (fulfill, reject) {
var root = game.root || '';
// get resources
var types = {
jpg : getImage,
png : getImage,
bmp : getImage,
mp3 : getAudio,
ogg : getAudio,
wav : getAudio,
json : getJSON
};
// wait for all resources to load
Promise.all(mapObjectToArray(game.resources, function (path, name) {
// get file extension
var extension = path.match(/(?:\.([^.]+))?$/)[1];
// find the getter
var get = types[extension];
// reject if there wasn't one
if (!get) return reject(Error('Unknown resource type "' + extension + '".'));
// get it and convert to 'object-able'
return get(root + path, name).then(function (resource) {
return {val : resource, name : name};
});
// someday I'll be able to do this
// return get(root + path, name).then(resource => ({val : resource, name : name}));
})).then(function (resources) {
// convert resources to object
resources = backToObject(resources);
// attach resources to window
window.resources = resources;
// sequentially load scripts
return game.scripts.reduce(function (queue, path) {
return queue.then(function () {
return getScript(root + path);
});
}, Promise.resolve()).then(function () {
// resources is final value of the whole promise
fulfill(resources);
});
});
});
};
So for these promises
const obj = { foo: promise1, bar: promise2 }
if you want object of values then await the promises
const newObj = {}
for (let key in obj)
newObj[key] = await obj[key]
but if you want an object of settled promises await them all without reassigning.
await Promise.all(Object.values(obj))
I recommend Sindre Sorhus' p-props. His stuff is always excellent.
One simple and easiest way to do it is
Promise.all([yourObject]).then((result)=>{
yourObject={...result}
}).catch((error)=>{console.log(error)})
Create function:
const promiseAllFromObject = async promisesObject => (
Object.keys(promisesObject).reduce(async (acc, key) => {
const lastResult = await acc;
return Object.assign(lastResult, { [key]: await promisesObject[key] });
}, Promise.resolve({}))
);
Usage:
promiseAllFromObject({
abc: somePromise,
xyz: someOtherPromise,
});
Result:
{
abc: theResult,
xyz: theOtherResult,
}
Need this, including good TypeScript support?
combine-promises can mix object values of different types and infer a good return type.
https://github.com/slorber/combine-promises
const result: { user: User; company: Company } = await combinePromises({
user: fetchUser(),
company: fetchCompany(),
});

Categories

Resources