Multiple queries end with unpredictably sorted result - javascript

There are few hundred documents in my database. Schema is very simple:
var firmsSchema = mongoose.Schema({
name: String,
sections: [String],
});
I want to query documents and iterate over:
{{#each sections}}
{{sectionName}}
{{#each firms}}
{{firmName}}
{{/each}}
{{/each}}
Simple:
const SECTIONS = ['name_one', 'name_two', 'name_three'];
const UNSORTED_SECTION_NAME = 'unsorted';
router.get('/', function(req, res, next) {
var showFirms = function showSection (i, acc) {
if (i < 0) return;
let query = SECTIONS[i] ? {sections: SECTIONS[i]} : {sections: {$nin: SECTIONS}};
let key = SECTIONS[i] || UNSORTED_SECTION_NAME;
Firms.find(query).
then((result) => {
acc.push({
section: key,
firms: result,
});
if (i === SECTIONS.length) {
acc = acc.sort((a, b) => (a.section > b.section));
res.render('template', {
sections: acc,
});
}
}).
then(showSection (i - 1, acc));
}
showFirms(SECTIONS.length, []);
};
Works fine. Except it returns acc randomly and unpredictably sorted. I mean 'name_two' section can follow 'name_one' or vise versa.
I thought .sort() at the end of promises chain would be a silver bullet here and solve all asynchronous problems, but it didn't.
Of course i can sort acc with handlebars helper after i pass it to my template, but it is so ridiculously strange i can't sort it right after all queries have been done in my showFirms function.
Can you give me some advise please?

Look at this remake of your code. Instead of getting the data one by one, we gotta get them on the same time (asynchronously) and then treat the return.
If you have any questions I am here, this code is untested so give me a feedback. This is an example of how you can change your code.
const showFirms = function showSection() {
return new Promise((resolve, reject) => {
// Get the keys for the queries
const keys = SECTIONS.map(x => x || UNSORTED_SECTION_NAME);
// For each sections we gonna call a find request
const promises = SECTIONS.map((x, xi) => {
const query = x ? {
sections: x,
} : {
sections: {
$nin: SECTIONS,
},
};
const key = keys[xi];
return Firms.find(query);
});
// Resolve all promises
Promise.all(promises)
.then((rets) => {
// Use the finds results to build an acc array
const accs = rets.map((x, xi) => ({
section: keys[xi],
firms: x,
}));
// Change the sort -> ;) #comments
const sortedAccs = accs.sort((a, b) => (a.section > b.section));
resolve(sortedAccs);
})
.catch(reject);
});
};
How to use it
showFirms()
.then(accs => res.render('template', {
sections: accs,
}))
.catch(err => console.log(`I have an error ${err.toString()}`));

Based on Grégory NEUTS excellent solution. I simplified some things and made the "other sections case" work. I even dropped out sort functionality. Eventual result returns in order of sections as they were declared in the initial SECTIONS array, so now i can just reorder it to control output.
const showFirms = function () {
return new Promise ((resolve, reject) => {
const extendedSections = SECTIONS.slice();
extendedSections.push(UNSORTED_SECTION_NAME);
const promises = extendedSections.map((section) => {
const unsortedCase = section === UNSORTED_SECTION_NAME;
const query = unsortedCase ? {sections: {$nin: SECTIONS}} : {sections: section};
return Firms.find(query);
})
Promise.all(promises)
.then((allResponces) => {
const sectionsData = allResponces.map((response, i) => ({
section: extendedSections[i],
firms: response,
}));
resolve(sectionsData);
})
.catch(reject);
});
};

Related

Node.js Express not waiting for Async Await

I have a program that I'm trying to run, and when getting the cars, it needs to wait for the function to finish before running the result, but it doesn't seem to be working. I'm new to this, so I don't really know what I'm doing all that well.
app.get('/cars', async (req, res) => {
try {
const result = await getCars()
console.log('result: ' + result)
res.send(result)
} catch(error) {
console.log(error)
}
})
That's the code to call the "getCars" function which is below:
const getCars = async () => {
// TODO: Replace this with a call to the database
await fs.readFile(__dirname + '/cars.json', function (err, data) {
if (err) {
throw err
}
let cars = JSON.parse(data)
//Groups the cars to their location, to be sorted and chosen based on arrivalDate
const groupBy = (xs, f) => {
return xs.reduce(
(r, v, i, a, k = f(v)) => ((r[k] || (r[k] = [])).push(v), r),
{}
)
}
const result = groupBy(cars, (c) => c.locationName)
Object.keys(result).forEach((car) => {
console.log(car)
// filters out cars with no arrival dates
let filtered = result[car].filter(obj => Object.keys(obj).includes("arrivalDate"));
//Sort cars by the last update time
filtered.sort(function (a, b) {
let keyA = new Date(a.arrivalDate.toString().split(' ')[0]),
keyB = new Date(b.arrivalDate.toString().split(' ')[0])
// Compare the 2 dates
if (keyA < keyB) return -1
if (keyA > keyB) return 1
return 0
}).reverse()
//Add the top two (latest) of each car to the new array
emailCars = [...emailCars, { [car]: [ result[car][0], result[car][1] ]}]
})
console.log('returning' + emailCars)
return emailCars
})
}
What am I missing here to make sure emailCars is being set by the function and then sent to the user when they go to /cars
I believe that problem is with the very first line of the getCars() function...
await fs.readFile(__dirname + '/cars.json', function (err, data) {
}
You cannot await a function that returns results in a callback. Either use "sync" version of readFile and remove await/async from getCars(), or use promisfied version of readFile:
try {
const data = await fs.promises.readFile(filepath, 'utf8');
} catch (e) {
// handle errors herer
}
The only issue that I see is you are mixing async/await with callback notion together. Just replace all the callbacks with async/await and wrap them around try/catch.
const getCars = async () => {
// TODO: Replace this with a call to the database
try {
const data = await fs.readFile(__dirname + '/cars.json')
let cars = JSON.parse(data)
//Groups the cars to their location, to be sorted and chosen based on arrivalDate
const groupBy = (xs, f) => {
return xs.reduce(
(r, v, i, a, k = f(v)) => ((r[k] || (r[k] = [])).push(v), r),
{}
)
}
const result = groupBy(cars, (c) => c.locationName)
Object.keys(result).forEach((car) => {
console.log(car)
// filters out cars with no arrival dates
let filtered = result[car].filter(obj => Object.keys(obj).includes("arrivalDate"));
//Sort cars by the last update time
filtered.sort(function (a, b) {
let keyA = new Date(a.arrivalDate.toString().split(' ')[0]),
keyB = new Date(b.arrivalDate.toString().split(' ')[0])
// Compare the 2 dates
if (keyA < keyB) return -1
if (keyA > keyB) return 1
return 0
}).reverse()
//Add the top two (latest) of each car to the new array
emailCars = [...emailCars, { [car]: [ result[car][0], result[car][1] ]}]
})
console.log('returning' + emailCars)
return emailCars
} catch (e) {
console.log(e);
}
}
If you have any difficulty understanding this concept, please check this link.
if I do that, it returns all the sorted data, but I only need the
first 2 of each sub-array in the object (thats what the emailCars
does, gets just the first 2 objects in each array). Or should I do
that in the /get function instead?
Any utility function should be correctly organised in order to maintain the correct structure of your application. The routes should not contain so much logic and the logic should be placed under controller or anything like that.
You need to devise your own logic and if you face any issues, you are welcome to ask on this platform.
You should first understand sync/asynchronous concept. Here is my solution.
import express from 'express'
import { readFile } from 'fs'
import { dirname } from 'path'
import { fileURLToPath } from 'url'
import { promisify } from 'util'
const app = express()
const readFileAsync = promisify(readFile)
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const getCars = async () => {
const file = await readFileAsync(`${__dirname}/cars.json`, 'utf8')
const cars = JSON.parse(file)
// DO YOUR LOGIC HERE
return cars
}
app.get('/', async (req, res) => {
try {
const cars = await getCars()
res.json(cars)
} catch (error) {
console.log(error)
}
})
app.listen(7777, () => {
console.log('Example app listening on port 7777!')
})

How to send res.json() in Express JS from a for loop

I am trying to send a json response when an API call is triggered from front-end, I'm not able to send the res.json() when I am getting the data from a for loop. Where I am writing a query to search in multiple Tables. I am using RethinkDB.
I want res.json() to send data after the query, But I don't understand what mistake I am doing. :(
Thanks in advence
zeasts
Here is the following code and Fiddle Link too.
const express = require("express");
const router = express.Router();
const moment = require('moment');
const r = require('rethinkdb');
const tableNameDB = ['assets', 'alerts', 'destinations']
router.post('/', (req, res, next) => {
let resData = []
let searchValue = req.body.searchValue,
tableName = req.body.tableName;
newCallForSearch(res, searchValue, resData)
})
function newCallForSearch (res, searchValue, resData){
let anArray = ['captain']
for(var i = 0; i<tableNameDB.length; i++){
let tabName = tableNameDB[i]
r.table(tableNameDB[i]).filter(function(doc) {
return doc.coerceTo('string').match(searchValue);
}).run(rethink_conn, (err, cur) => {
// console.log(cur)
if (err) {
return 0
} else {
cur.toArray((err, result) => {
if (err) {
return 0
} else if (result) {
let Results = []
Results = Object.values(result).slice(0,10)
var newResults = Results.map(function() {
resData = Object.assign({'tableName': tabName},{'data' : result});
anArray.push(resData)
})
}
})
}
})
}
res.status(200);
res.json(anArray);
}
module.exports = router;
RethinkDb is a functional database and so using it in a functional way will yield the least resistance. We can accomplish more by writing less code.
You can use Promise.all to run many subqueries and pass the result to res.send -
const search = (table = "", query = "", limit = 10) =>
r.table(table)
.filter(doc => doc.coerceTo("string").match(query))
.toArray()
.slice(0, limit)
.do(data => ({ table, data }))
const tables =
['assets', 'alerts', 'destinations']
const subqueries =
tables.map(t => search(t, "foo").run(rethink_conn)) // <-- search each table
Promise.all(subqueries) // <-- run all subqueries
.then(result => { // <-- handle success
res.status(200)
res.json(result)
})
.catch(e => { // <-- handle failure
res.status(500)
res.send(e.message)
})
Or use .union to produce a single query -
const search = (table = "", query = "", limit = 10) =>
r.table(table)
.filter(doc => doc.coerceTo("string").match(query))
.toArray()
.slice(0, limit)
.do(data => ({ table, data }))
const searchAll = (tables = [], query = "", limit = 10) =>
tables.reduce
( (r, t) => r.union(search(t, query, limit)) // <-- union
, r.expr([]) // <-- if no tables are supplied, return empty result
)
const tables =
['assets', 'alerts', 'destinations']
searchAll(tables, "foo") // <-- single rethink expr
.run(rethink_conn) // <-- run returns a promise
.then(result => { // <-- handle success
res.status(200)
res.json(result)
})
.catch(e => { // <-- handle failure
res.status(500)
res.send(e.message)
})
I should remark on the proposed use of filter in your original post -
.filter(doc => doc.coerceTo("string").match(query))
This is quick but it is also sloppy. It matches query against any of docs values, but also the doc's keys. And if doc is a complex nested document, it matches them too. User beware.

Recursion and Observable RxJs

I am performing pagination inside and Observable stream.
The pagination is implemented with a cursor and a total count using recursion.
I am able to emit the every page using the following code observer.next(searches);, by the way I would like to use just observable and no promises but I cannot express recursion using RxJs operators.
Any suggestions?
const search = id =>
new Observable(observer => { recursePages(id, observer) })
const recursePages = (id, observer, processed, searchAfter) => {
httpService.post(
"http://service.com/search",
{
size: 50,
...searchAfter ? { search_after: searchAfter } : null,
id,
})
.toPromise() // httpService.post returns an Observable<AxiosResponse>
.then(res => {
const body = res.data;
const searches = body.data.hits.map(search => ({ data: search.data, cursor: search.id }));
observer.next(searches);
const totalProcessed = processed + searches.length;
if (totalProcessed < body.data.total) {
return recursePages(id, observer, totalProcessed, searches[searches.length - 1].cursor);
}
observer.complete();
})
}
// General Observer
incomingMessages.pipe(
flatMap(msg => search(JSON.parse(msg.content.toString()))),
concatAll(),
).subscribe(console.log),
these methods will recursively gather all the pages and emit them in an array. the pages can then be streamed with from as shown:
// break this out to clean up functions
const performSearch = (id, searchAfter?) => {
return httpService.post(
"http://service.com/search",
{
size: 50,
...searchAfter ? { search_after: searchAfter } : null,
id,
});
}
// main recursion
const _search = (id, processed, searchAfter?) => {
return performSearch(id, searchAfter).pipe( // get page
switchMap(res => {
const body = res.data;
const searches = body.data.hits.map(search => ({ data: search.data, cursor: search.id }));
const totalProcessed = processed + searches.length;
if (totalProcessed < body.total) {
// if not done, recurse and get next page
return _search(id, totalProcessed, searches[searches.length - 1].cursor).pipe(
// attach recursed pages
map(nextPages => [searches].concat(nextPages)
);
}
// if we're done just return the page
return of([searches]);
})
)
}
// entry point
// switch into from to emit pages one by one
const search = id => _search(id, 0).pipe(switchMap(pages => from(pages))
if what you really need is all of the pages to emit one by one before they're all fetched, for instance so you can show page 1 as soon as it's available rather than wait on page 2+, then that can be done with some tweaking. let me know.
EDIT: this method will emit one by one
const _search = (id, processed, searchAfter?) => {
return performSearch(id, searchAfter).pipe( // get page
switchMap(res => {
const body = res.data;
const searches = body.data.hits.map(search => ({ data: search.data, cursor: search.id }));
const totalProcessed = processed + searches.length;
if (totalProcessed < body.total) {
// if not done, concat current page with recursive call for next page
return concat(
of(searches),
_search(id, totalProcessed, searches[searches.length - 1].cursor)
);
}
// if we're done just return the page
return of(searches);
})
)
}
const search = id => _search(id, 0)
you end up with an observable structure like:
concat(
post$(page1),
concat(
post$(page2),
concat(
post$(page3),
post$(page4)
)
)
)
and since nested concat() operations reduce to a flattened structure, this structure would reduce to:
concat(post$(page1), post$(page2), post$(page3), post$(page4))
which is what you're after and the requests run sequentially.
it also seems like expand might do the trick as per #NickL 's comment, soemthing like:
search = (id) => {
let totalProcessed = 0;
return performSearch(id).pipe(
expand(res => {
const body = res.data;
const searches = body.data.hits.map(search => ({ data: search.data, cursor: search.id }));
totalProcessed += searches.length;
if (totalProcessed < body.data.total) {
// not done, keep expanding
return performSearch(id, searches[searches.length - 1].cursor);
}
return EMPTY; // break with EMPTY
})
)
}
though I've never used expand before and this is based off some very limited testing of it, but I am pretty certain this works.
both of these methods could use the reduce (or scan) operator to gather results if you ever wanted:
search(id).pipe(reduce((all, page) => all.concat(page), []))
This is my used solution combining the expand and reduce operator
searchUsers(cursor?: string) {
return from(
this.slackService.app.client.users.list({
token: this.configService.get('SLACK_BOT_TOKEN'),
limit: 1,
...(cursor && { cursor }),
}),
);
}
Usage
.......
this.searchUsers()
.pipe(
expand((res) => {
if (!!res.response_metadata.next_cursor) {
return this.searchUsers(res.response_metadata.next_cursor);
}
return EMPTY;
}),
reduce((acc, val) => {
return [...acc, ...val.members];
}, []),
)
.subscribe((users) => {
console.log(JSON.stringify(users));
});
....

How to wait for internal promises to finish

I am using fs.readdir to get a list of directories and then again in the callback to get a list of "subpages" in each of these directories. I would like for the first callback to wait until the second callback is completed but I'm not sure how to do that.
// Array to hold list of pages
const pageList = []
// Get contents of target directory (not recursive)
fs.readdir(targetDir, { withFileTypes: true }, (err, items) => {
// Stop and return if error
if (!!err) return err
// Go through found contents
const theseItems = items.map(item => {
const subpages = []
// Directory name
const dirName = item.name
// Set up published target for this directory
const thisTargetDir = targetDir + '/' + dirName + '/publish'
// Now get pages in the directory's published directory
// (assumes all files within subdirectories are .mdx pages to load)
return (
fs.readdir(thisTargetDir, { withFileTypes: true }, (err, pages) => {
const theseSubpages = pages.map(page => {
const mdxSuffix = /.mdx$/g
const pageName = page.name.replace(mdxSuffix, '')
return subpages.push({ name: pageName })
})
Promise.all(theseSubpages).then(() => {
// Add to page list array
pageList.push({ name: dirName, subpages: subpages })
})
})
)
})
Promise.all(theseItems).then(() => {
console.log('pageList at the end is: ')
console.log(pageList)
})
})
The Promise.all(theseSubpages) works as expected, however the Promise.all(theseItems) resolves before the former has a chance to cycle through. I understand why that's happening and I've tried to do things like return each item as a Promise.resolve(), etc. but these things aren't working.
Wondering if I'm doing something inherently wrong in this approach…
UPDATE
I tried using the fsPromises approach but kept running into the same wrong patterns. Ended up using the node-dir package to go through the directories recursively. Code below, not really the exact answer to what I was trying to do, but this gets the result I was looking for.
const dir = require('node-dir')
const targetDir = __dirname + '/../pages/stuff'
const pageList = []
dir.paths(targetDir, (err, paths) => {
if (err) throw err
const baseMatch = __dirname.replace('/lib', '') + '/pages/stuff'
paths.dirs.map(dir => {
// Only publish paths
if (dir.substr(-7) === 'publish') {
// Get the slug directly before publish path
const contentSlug = dir.split('/').slice(-2)[0]
// Add this to main pageList array as top level objects
pageList.push({ name: contentSlug, subpages: [] })
}
})
paths.files.map(file => {
const filePathArray = file.split('/')
// Only publish paths
if (filePathArray.slice(-2)[0] === 'publish') {
// Get parent content slug for matching purposes
const parentContentSlug = filePathArray.slice(-3)[0]
// Get file name (remove .mdx suffix)
const mdxSuffix = /.mdx$/g
const fileName = filePathArray.slice(-1)[0].replace(mdxSuffix, '')
// Loop through main page list, find match, then add file as subpage
pageList.find((obj, key) => {
if (obj.name === parentContentSlug) {
return pageList[key].subpages.push({ name: fileName })
}
})
}
})
console.log('pageList at end:')
console.log(pageList)
})
Promises work by chaining .then calls (Promise.then(doStuff)). If you start a promise but then don't chain, you can't know when it's done. In order to chain promises from inner functions you have to return promises.
Generally you don't want to mix callbacks and promises.
If I were to do this I would start by using just promises.
const readdir = (target, options) =>
// returns a promise that resolves or rejects when the call finishes
new Promise((resolve, reject) =>
fs.readdir(target, options, (err, result) => {
if (err) reject(err);
resolve(result);
})
);
const collectSubPages = pages =>
// Wait for all the promises in the array to resolve
Promise.all(
// for each page, return a promise that resolves to the page/subpage object
pages.map(({ name }) =>
readdir(targetDir + "/" + name + "/publish", {
withFileTypes: true
})
.then(subpages => subpages.map(({ name }) => ({ name })))
.then(subpages => ({ name, subpages }))
)
);
readdir(targetDir, { withFileTypes: true })
.then(pages => collectSubPages(pages))
.then(console.log);
#David Yeiser, your own "update" code can be written more concisely using Array methods .filter() and .map(), plus various optimisations, as follows :
const dir = require('node-dir');
const targetDir = __dirname + '/../pages/stuff';
dir.paths(targetDir, (err, paths) => {
if (err) {
throw err;
}
const baseMatch = __dirname.replace('/lib', '') + '/pages/stuff';
const mdxSuffix = /.mdx$/g; // define once, use many times
const fileList = paths.files
.map(fileName => fileName.split('/'))
.filter(filePathArray => filePathArray[filePathArray.length - 2] === 'publish'); // Only 'publish' paths
const pageList = paths.dirs
.filter(dir => dir.substr(-7) === 'publish') // Only 'publish' paths
.map(dir => {
const name = dir.split('/').slice(-2)[0];
const subpages = fileList
.filter(filePathArray => filePathArray[filePathArray.length - 3] === name) // select those files whose "parent content slug" matches 'name'
.map(filePathArray => filePathArray[filePathArray.length - 1].replace(mdxSuffix, ''));
return { name, subpages };
});
console.log('pageList at end:');
console.log(pageList);
});
You will see that :
fileList is constructed with a paths.files.map().filter() pattern.
pageList is constructed with a paths.dirs.filter().map() pattern.
for each entry in pageList, subpages is constructed with a fileList.filter().map() pattern.
Barring mistakes on my part, that should give the same result.
untested

Beautiful way to resolve an object with nested promises?

While building custom endpoints I often need to resolve a complex object containing promises.
For illustration, take this example:
Given known user's id, employeeId and memberGroupsIds (an array):
var loginResponse = {
userprofile : getProfile(id)
companyInfo : {
company : getCompany(employeeId)
companyRelations : getPriviligedInfo(employeeId)
}
groups : getGroups(memberGroupsIds)
}
This logic works for synchronous functions that just return their values. But with functions that return promises I have to manually push all of them into an array to ensure they are resolved before using the final object.
I find the above code very easy to understand, and I'm looking for a signature that gives some of that, while still ensuring that the promises are resolved before sending a final object to the client.
The problem is not making it work, but making it beautiful and easy to read.
The best answer would ensure that the values are returned to the expected keys in the object and that all the promises are resolved in parallel, while maintaining a structure that is somewhat compatible with that of synchronous functions.
Or, if I'm missing the point and looking at this all wrong, how should I be looking at it?
You could use the helper function below. It takes an object and returns a promise that resolves when all nested promises have been resolved. The returned promise will provide as value the same object, which will have mutated with all its embedded promises replaced by their corresponding values.
function promiseRecursive(obj) {
const getPromises = obj =>
Object.keys(obj).reduce( (acc, key) =>
Object(obj[key]) !== obj[key]
? acc
: acc.concat(
typeof obj[key].then === "function"
? [[obj, key]]
: getPromises(obj[key])
)
, []);
const all = getPromises(obj);
return Promise.all(all.map(([obj, key]) => obj[key])).then( responses =>
(all.forEach( ([obj, key], i) => obj[key] = responses[i]), obj)
);
}
You would call it like this:
var loginResponsePromise = promiseRecursive({
userprofile : getProfile(10),
companyInfo : {
company : getCompany(101),
companyRelations : getPriviligedInfo(101)
},
groups : getGroups([5])
});
function promiseRecursive(obj) {
const getPromises = obj =>
Object.keys(obj).reduce( (acc, key) =>
Object(obj[key]) !== obj[key] ? acc
: acc.concat(typeof obj[key].then === "function" ? [[obj, key]]
: getPromises(obj[key]))
, []);
const all = getPromises(obj);
return Promise.all(all.map(([obj, key]) => obj[key])).then( responses =>
(all.forEach( ([obj, key], i) => obj[key] = responses[i]), obj)
);
}
// Example promise-returning functions
const wait = ms => new Promise( resolve => setTimeout(resolve, ms) ),
getProfile = id => wait(100).then(_ => ({userName: 'user' + id,id})),
getCompany = employeeId => wait(200).then(_ => ({employeeName: 'employee' + employeeId, employeeId})),
getPriviligedInfo = employeeId => wait(500).then(_ => ({privs: 'privInfo' + employeeId, employeeId})),
getGroups = memberGroupsIds => wait(400).then(_ => ({groups: ['group' + memberGroupsIds[0]],memberGroupsIds}));
// Sample input passed to `promiseRecursive` function
const loginResponsePromise = promiseRecursive({
userprofile : getProfile(10),
companyInfo : {
company : getCompany(101),
companyRelations : getPriviligedInfo(101)
},
groups : getGroups([5])
});
// Display the resolved object
loginResponsePromise.then( o => console.log(o) );
.as-console-wrapper { max-height: 100% !important; top: 0; }
I usually solve this kind of scenarios with Bluebird's join http://bluebirdjs.com/docs/api/promise.join.html :
const Promise = require('bluebird');
return Promise.join(
getProfile(id),
getCompany(employeeId),
getPrivilegedInfo(employeeId),
getGroups(memberGroupsIds),
(userProfile, company, companyRelations, groups) => {
return {
userProfile: userProfile,
companyInfo: {
company: company,
companyRelations: companyRelations
},
groups: groups
};
}
);
Using new ES6 features I would write something like this:
Promise.all([
getProfile(id),
getCompany(employeeId),
getPriviligedInfo(employeeId),
getGroups(memberGroupsIds)
])
.then(response => {
const [ userprofile, company, companyRelations, groups ] = response
const loginResponse = {
userprofile,
companyInfo : {
company,
companyRelations
},
groups
}
})
.catch(err => console.error(err))
Maybe the interesting part is that Promise.all() keep the input arguments order not depending on which resolves first. So in next step, using Destructuring Array assignment, the code looks like synchronous.

Categories