This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I am not really a Promise Ninja and I understand that I'm doing something wrong. However I cannot find some particular/simular problem to what I am having.
The problem: I use the Dexie.js wrapper for IndexedDB which is asynchronous. I have a global database which leads to some other dexie databases.
function handleDatabases() {
var result = [];
db.jobs.orderBy('title').filter(function(job) {
return job.someBooleanCondition;
}).each(function(job, cursor) {
let jobDetails = new Dexie(job.correspondingDB);
jobDetails.version(1).stores({
details: 'key,value1,value2'
});
jobDetails.details.get(someKey).then(function(detail) {
result.push({job: job, detail: detail});
})
}).catch(function(error) {
console.log(error);
});
handleResult(result);
}
I have rewritten it for SO with a maybe strange form but the end goal is that i can use the array result to handle some update. However since it is asynchronous it is always empty until you inspect it in console where it is never empty. How can I rewrite this to be synchronous?
You cannot expect to return the result when that result only becomes available asynchronously.
So you must stick with promises all the way (returning them each time), and let your function also return a promise. The caller must use then (or await if supported) to allow (asynchronous) access to the result.
Instead of pushing the {job: job, detail: detail} to a results variable, return it. It will become the promised value for jobDetails.details.get(..).then(..). If you return also that, you'll have an array of promises, which can then be resolved with Promise.all
Avoid to create new Promises as that usually leads to the promise constructor antipattern.
Also avoid using a variable (like results) that is used in several callbacks without being passed as argument. Instead try to construct and return that array as a promised value, so it can be used in the next then callback.
Here is the suggested (untested) code:
function handleDatabases() {
db.jobs
.orderBy('title')
.filter(job => job.someBooleanCondition)
.toArray(jobs =>
jobs.map(job => {
let jobDetails = new Dexie(job.correspondingDB);
jobDetails.version(1).stores({
details: 'key,value1,value2'
});
return jobDetails.details.get(someKey)
.then(detail => ({job: job, detail: detail}))
}) // is returned
)
.then(result => Promise.all(result))
.then(handleResult)
.catch(error => console.log(error));
}
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 1 year ago.
I am having a downstream API call which is basically returning a Promise object instead of resolving it and executing it on point.
This is how I am calling the downstream:
const response = testClient.getSession(sessionId);
When I console.log(response) it is prinitng as Promise { <pending> } instead of desired result which it shoudl print from the downstream.
Having known very little about Async/Await, Promises etc I would like to know whihc category does it fall in? And how do I execute that promise first and do the rest of the steps.
However, I have found a temporary work around like this:
response.then(function(result) {
console.log(result.body);
});
But ideally I would want to store the result into response object then and there itself. Please help me understand this. Thanks in advance
What you called a temporary workaround using then is actually the correct way of waiting on the Promise to be fulfilled. You cannot 'force' it to be fulfilled, because the code will execute as fast as it can and allow you to carry on working until then (that's the point of promises) - but you can wait on it to be fulfilled:
You ought also to check to see if the promise gets 'rejected' using catch - ie produces an error:
response.then( function(result) {
console.log(result.body);
}).catch( function(error) {
console.log(error);
});
To use await:
async function thingy(){
const response = await testClient.getSession(sessionId);
}
To use await and check for errors:
async function thingy(){
try {
const response = await testClient.getSession(sessionId);
} catch (error) {
console.error(error);
}
}
Note that when using async, you do generally need to be in a function declared as async.
The idea of the Promise is to allow your program to carry on doing things whilst you wait for the promise to be fulfilled/rejected - for example, you might make several requests at once, and wait for them all:
Promise.all([
getStuff('one'),
getStuff('two'),
getStuff('n')
]);
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
Have a subscription thats being called after everything else gets set but i wanna wait till the subscription finshes. T
Tried using async await but that didnt work with me. No sure if I was doing it incorrectly
public getGlobeConfig() {
this.sourceKey = 'test';query = 'query'
// wait till this call finishes than hit the
console.log('htting')
this.service
.searchQuery(query)
.subscribe(response => {
this.sources = response.hits.hits.map(hit =>
hit._source);
if (this.sources.length > 0) {
this.availableMetadata = Object.keys(
this.sources[0].metadata[this.sourceKey]
);
}
});
console.log('hitting')
return this.sources
}
This.sources is reaching as undefined because this.sources is being set in the subscriptioon
The short answer is you cannot cause the code after the subscribe to wait. Having said that, by taking a step back and looking at your code, you should not subscribe within the getGlobeConfig method. What you probably should do is use the map operator within the getGlobeConfig method and let the consumer of the getGlobeConfig method subscribe:
public getGlobeConfig() {
// ...
return this.service.searchQuery(query).pipe(
map(response => {
// Transform the response to what you want
// the consumer of this method to receive
})
);
}
Consumer:
getGlobeConfig().subscribe(sources => /* ... */)
One very common pitfall I am seeing from new RxJs developers is that they try to subscribe to Observables in a service and then want to return the data to the components. In most cases you do not subscribe within the services. Let the services operate on the data within RxJs operators and have the services return the transformed Observables. The end consumer (usually components) will then subscribe to the Observables returned by the services.
Your problem is that you cannot return a synchronous value that is generated in an asynchronous call. The best you can do is return a promise (or other async object). This is what async await is designed to accomplish: it adds keywords that make it easier to wait for promises to finish but in the end you are still working with promises, and async functions always return promises.
Here's some simple examples:
function doSomethingWithPromise() {
return somethingThatReturnsAPromise()
.then((result) => result.calculateSomethingFromResult()) // `then` returns a promise with a result of the callback
}
Transformed to an async call:
async function doSomethingWithAsync() {
// because this is an async function, it returns a promise here, before it finishes waiting
let result = await somethingThatReturnsAPromise()
return result.calculateSomethingFromResult() // at this point, after waiting, the function fulfills the promise with this return value
}
Those two examples are equivalent.
(This is a general example, and if you are using a library that uses streams or events instead of promises, for example, things may be different)
This question already has answers here:
How do I access previous promise results in a .then() chain?
(17 answers)
Closed 5 years ago.
I am writing a Node.js script to populate an SQL database with a test dataset.
In the promise chain illustrated in the code snippet below (the real code is a bit more hairy), the function insertData() requires the db object to be passed through from the previous stage. However, asynchronous calls inside dropAndCreateTables() on the previous stage use db object but do not return it. I came up with a solution that wraps promises inside dropAndCreateTables() into another promise object that resolves to a db object. However:
I heard that using Promise() constructor in non-library code is an antipattern and may lead to subtle and hard-to-diagnose mistakes
I heard that nesting then()-chains is also an antipattern
It does not allow me to ignore errors from the promiseDrop (for example, I don't care if tables don't exist on drop)
It is ugly
Questions:
Is there a simpler, nicer and more socially accepted way to override the return value of a promise? (in this case, created with Promise.all())
Is there a way to restructure my code in a way that this problem does not occur? (That is, I don't exclude the possibility of "XY problem" here)
Code:
const dropAndCreateTables = (db, startClean) => {
if(startClean) {
const sqlDrop = fs.readFileSync('drop.sql').toString()
const promiseDrop = db.raw(sqlDrop)
const sqlCreate = fs.readFileSync('create.sql').toString()
const promiseCreate = db.raw(sqlCreate)
/********* Problems here? ************************************/
return new Promise((resolve, reject) => { // Ew?
Promise.all([promiseDrop, promiseCreate])
.then(() => {
resolve(db) // Override the returned value
})
.catch(reject)
})
}
return Promise.resolve(db)
}
initDB({ debug: false })
.then((db) => {
return dropAndCreateTables(db, START_CLEAN) // Without my hack this does not return `db`
})
.then((db) => {
return insertData(db, DO_UPSERT) // This needs the `db` object
})
.then(() => {
console.info(`\n${timestamp()} done`)
})
.catch(handleError)
(Some fairly important notes midway and later in the answer, please do read all the way to the end.)
Is there a simpler, nicer and more socially accepted way to override the return value of a promise? (in this case, created with Promise.all())
Yes, you simply return a value from the then handler, and return the promise then returns:
return Promise.all([promiseDrop, promiseCreate])
.then(() => db);
then (and catch) create promise chains. Each link in the chain can transform the result. then and catch return a new promise that will be fulfilled or rejected based on what happens in their callback:
If their callback throws, the promise rejects with the error thrown
If their callback returns a non-thenable value (e.g., promise), the promise is fulfilled with that value
If their callback returns a thenable value, the promise is resolved to that thenable — it waits for the other promise to settle, then settles the same way
(If the term "thenable" isn't familiar, or you're not clear on the distinction between "fulfill" and "resolve," I go into promise terminology in this post on my blog.)
I heard that using Promise() constructor in non-library code is an antipattern and may lead to subtle and hard-to-diagnose mistakes
The distinction isn't library code vs. non-library code, it's between code that doesn't already have a promise to work with and code that does. If you already have a promise to work with, you almost never want to use new Promise. More: What is the explicit promise construction antipattern and how do I avoid it?
I heard that nesting then()-chains is also an antipattern
You almost never need to nest then chains, because again each link in the chain already has the means of tranforming the result passing through it. So:
// Unnecessary nesting
doSomething()
.then(a => {
return doSomethingElse(a * 2)
.then(b => b * 3);
})
.catch(e => { /*...handle error...*/ });
can be more idiomatically and simply written:
doSomething()
.then(a => doSomethingElse(a * 2))
.then(b => b * 3);
.catch(e => { /*...handle error...*/ });
Is there a way to restructure my code in a way that this problem does not occur? (That is, I don't exclude the possibility of "XY problem" here)
Not an X/Y per se, but you have a problem in that code: There's no guarantee the drop will happen before the create! So instead of starting both and letting them run in parallel and watching for the results with Promise.all, ensure those operations happen in sequence:
// Fairly minimal changes
const dropAndCreateTables = (db, startClean) => {
if(startClean) {
const sqlDrop = fs.readFileSync('drop.sql').toString()
return db.raw(sqlDrop)
.then(() => {
const sqlCreate = fs.readFileSync('create.sql').toString()
return db.raw(sqlCreate);
})
.then(() => db);
}
return Promise.resolve(db)
}
But, I wouldn't use sync file I/O. Instead
const promisify = require("utils").promisify;
const readWithPromise = promisify(fs.readFile);
and then
const dropAndCreateTables = (db, startClean) => {
if(startClean) {
const getDrop = readWithPromise('drop.sql'); // Start this first
const getCreate = readWithPromise('create.sql'); // Then start this
return getDrop
.then(dropSql => db.raw(dropSql)) // Got the drop SQL, run it
.then(() => getCreate) // Make sure we have the create SQl
.then(createSql => db.raw(createSql)) // Run it
.then(() => db);
}
return Promise.resolve(db)
}
Note how we avoid ever busy-waiting on I/O, and we can overlap the DB's drop operation with reading the create SQL.
You don't need to call the Promise constructor when returning another promise, you can just write it like:
return Promise.all([promiseDrop, promiseCreate])
.then(() => db)
.catch(error => {
// handle the error or rethrow it
})
You might omit resolving db from dropAndCreateTables like this:
.then((db) => {
return dropAndCreateTables(db, START_CLEAN).then(Promise.resolve(db));
})
You should not let dropAndCreateTables return a db promise, there is no real usecase for it. So:
return Promise.all([promiseDrop, promiseCreate]);
is enough. Now the chaining part:
initDB({ debug: false }).then(async (db) => {
await dropAndCreateTables(db, START_CLEAN);
await insertData(db, DO_UPSERT);
console.info(`\n${timestamp()} done`)
}).catch(handleError)
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I'm starting to work with raw promises and async/await in Node.js.
I have 2 "promis-ified" functions that I want to run in parallel, and then() perform some manipulation on the result and return the new data. The return value is always undefined, but inside the .then() the value is what I expect.
This is my function:
const fs = require('fs-promise-util').default;
/**
* compares 2 directories to find hooks of the same name
*
* #return {Array} hooks that exist in remote and local directories
*/
function remoteVSlocal () {
try {
Promise.all([
fs.readdir(REMOTE_HOOKS_PATH),
fs.readdir(LOCAL_HOOKS_PATH)
]).then(function ([REMOTE_HOOKS, LOCAL_HOOKS]) {
//filter out values that exist in both arrays
//this returns a new array with the values I expect
return LOCAL_HOOKS.filter(function (name) {
return REMOTE_HOOKS.includes(name);
});
});
} catch (err) {
return err;
}
}
When I call the function it returns undefined:
console.log(remoteVSlocal());
I expect a call to remoteVSlocal() to return the new array created by Array.filter().
Your function remoteVSlocal() doesn't actually return anything which is why the return value is undefined. You need to return the promise and use that returned promise when you call the function. Returning a value from an embedded .then() handler doesn't return from the function itself.
Here's a working version of your code assuming that fs.readdir() does actually return a promise (which btw is a horrible practice to take an existing standard API and change it's functionality - there are much better ways to promisify whole libraries).
Anyway, here's code that would work for you:
function remoteVSlocal () {
return Promise.all([
fs.readdir(REMOTE_HOOKS_PATH),
fs.readdir(LOCAL_HOOKS_PATH)
]).then(function ([REMOTE_HOOKS, LOCAL_HOOKS]) {
//filter out values that exist in both arrays
//this returns a new array with the values I expect
return LOCAL_HOOKS.filter(function (name) {
return REMOTE_HOOKS.includes(name);
});
});
}
In addition, you need to return the promise from remoteVSlocal() and then use the returned promise:
remoteVSLocal().then(result => {
// use result here
}).catch(err => {
// process error here
});
Summary of changes:
Return the promise from remoteVSlocal()
When calling remoteVSlocal() use the returned promise with .then() and .catch().
Remove try/catch since there are no synchronous exceptions here. Promises will propagate errors via a rejected promise.
I have a sequence of events I'm trying to align, but I don't understand how to resolve a promise after a single line of asynchronous code. In this case, I'm making an API call which resolves to an array of objects. I'm then using Object.assign() to convert the array of objects into an object of objects. Once completed, I then want to call a function which does something with that new object of objects. The conversion process takes some time, and I'm not sure how to postpone my function call until after the object has been converted. My non-functioning code is given below.
this.queryCodes().then(() => {
new Promise((resolve) => {
resolve(() => {
// This code isn't executing.
this.floorCodesLookup = Object.assign({}, ...this.floorCodes);
});
}).then((data) => {
this.resolveCodesToDescriptions();
});
});
resolve should be given the result of your asynchronous operation, not the code to execute. The code you want to execute shouldn't go in the resolve argument, but instead in the callback you give to the promise constructor. So for your snippet:
this.queryCodes().then(() => {
return new Promise((resolve) => {
// Put the code you want to execute here...
// Call resolve when your work is done, and pass it the result if there is one.
resolve(/* maybe result here */);
}).then((data) => {
this.resolveCodesToDescriptions();
});
});
It's also worth noting that you don't need to use a promise constructor since you're already chaining off a promise to begin with. You could just:
this.queryCodes().then(() => {
// Do additional work here...
this.floorCodesLookup = Object.assign({}, ...this.floorCodes);
return this.resolveCodesToDescriptions();
});
resolve() doesn't take a callback as an argument. It takes the resolved value as an argument. Not sure where you got the callback notion from. That's why the callback is never called because that's not a supported feature of resolve().
Manually creating a promise inside a .then() handler is likely not needed and is probably an anti-pattern. You don't show enough of the real code here to understand what is going on, but this is likely way more complicated than it needs to be. From the code you show, I think you can just do this:
this.queryCodes().then(() => {
this.floorCodesLookup = Object.assign({}, ...this.floorCodes);
this.resolveCodesToDescriptions();
});