knex promises - how to wait while two funcions finish their work - javascript

How to add code that will run only when both processes are completed?
normalise1();
normalise2();
function normalise1() {
return knex("ingredients")
.select("name", "id")
.map(function (ingredient) {
var normalised_name = common.normalise(ingredient.name);
knex('ingredients').where('id', ingredient.id).update({ name_normalised: normalised_name }).then();
});
};
function normalise2() {
return knex("synonyms")
.select("synon_name as name", "id")
.map(function (ingredient) {
var normalised_name = common.normalise(ingredient.name);
knex('synonyms').where('id', ingredient.id).update({ synon_name_normalised: normalised_name }).then();
});
};
I tried something like in different ways
Promise.all([normalise1(), normalise2()])
.then(() => console.log('Done'));
but it didn't work.
Basically console.log('Done') appears before all process is done. I believe that this is because of missing Promise part inside functions, but I cannot figure out exactly how.

The functions are not called when passed to Promise.all(), no Promise is returned from .map().
Call the functions and return knex() from .map() call, which may also require using Promise.all() within the function calls.

Related

How to resolve Promises in recursive JSON

I need to recursively go through JSON and in some cases call remote API. I need to return the whole JSON modified at the end but I cannot figure out how to wait until all promises are fulfilled
const getObjectsOfRelated = (xmlAsJson, token) => {
if (testIfIwantCallApi()) {
const jsonToReturn = JSON.parse(JSON.stringify(xmlAsJson))
jsonToReturn.elements = callApi(xmlAsJson.text).then(result => {
return result.data
})
return jsonToReturn
}
if (xmlAsJson.elements) {
const jsonToReturn = JSON.parse(JSON.stringify(xmlAsJson))
jsonToReturn.elements = xmlAsJson.elements.map(res => getObjectsOfRelated(res, token))
return jsonToReturn
}
return xmlAsJson
}
Even if I try to hack it using setTimeout the result does not include parts that were created using external API.
This way the code returns correct structure with promises instead of values I want it either return completed promises or be able to wait until the promises are fulfilled.
Wrap plain return values in Promise's using Promise.resolve:
const getObjectsOfRelated = (xmlAsJson, token) => {
if (testIfIwantCallApi()) {
const jsonToReturn = JSON.parse(JSON.stringify(xmlAsJson))
return callApi(xmlAsJson.text).then(result => {
jsonToReturn.elements = result.data;
return jsonToReturn;
})
}
if (xmlAsJson.elements) {
const jsonToReturn = JSON.parse(JSON.stringify(xmlAsJson))
Promise.all(xmlAsJson.elements.map(res => getObjectsOfRelated(res,
token)).then((results) => {
jsonToReturn.elements = results.map(result => result.data);
return jsonToReturn;
});
}
return Promise.resolve(xmlAsJson);
}
This way you will consistently return promises and you can use your function like this:
getObjectsOfRelated(xmlAsJson, token).then(result => console.log(result))
You can use "Promise.all"...
For a simple array, you map a function over the array:
The function returns a promise for the "new value" of each element.
If you use Bluebird promises, you can even return a mixture of Promises and plain values.
( without having to wrap plain values in "Promise.resolve" )
Then you pass the array of promises to "Promise.all()", which waits for all of them to complete.
To transform a tree-shaped data structure (like JSON), you do the same sort of thing, but recursively. Each node in the tree would use "Promise.all" to wait for all its child-nodes=, and the root node would only "resolve" when every node in the tree has resolved.
Note that "Promise.all" is going to run all of your ASYNC functions at the same time. If you don't want that, you can instead use "Promise.mapSeries", which does the same thing, but it waits for each async function before starting the next. This can be better if you have large data and don't want to start too many simultaneous async functions at the same time.

Call function after getting response from WS

I need to call three WS services before calling a local function depending on whether some variables are defined or not, but the function is getting called before the services get any response, because it could take some time. I've even tried with $timeout, but it does not work
$scope.$on('search', function (event, data) {
self.searchDto= data;
if (self.searchDto.userCode) {
self.searchByUserCode(self.searchDto.userCode).then(function (data) {
self.userCode= data.find(function (item) {
return item.mstId === self.searchDto.userCode;
});
});
}
if (self.searchDto.companyCode) {
self.serachByCompanyCode(self.searchDto.companyCode).then(function (data) {
self.companyCode= data.find(function (item) {
return item.mstId === self.searchDto.companyCode;
});
});
}
if (self.searchDto.jobCode) {
self.searchByJobCode(self.searchDto.jobCode).then(function (data) {
self.jobCode= data.find(function (item) {
return item.mstId === self.searchDto.jobCode;
});
});
}
//I tried with this timeout but it didnt work
$timeout(function () {
self.searchPeople();
}, 1000);
});
Does anyone have idea how the searchPeople method can be called after the WS responses?
Use promises and $q.all()
var promises = [];
promises.push(self.searchByUserCode(self.searchDto.userCode).then(function (data) {
self.userCode= data.find(function (item) {
return item.mstId === self.searchDto.userCode;
});
}));
.then() returns a promise. Do that for the 3 service calls and then wait for their completion
$q.all(promises).then(function(){
self.searchPeople();
})
I see that you might not call all of your services. $q.all() will wait for the promise you put in the array. Keep in mind it will also execute your call if none of your services has been executed, if you need at least one to be called, you might want to add a check for promises.length > 0 before $q.all().
That way, if you only call one of your services, the promises array will have one element and upon its completion, will call your local function.
Setting timeout is not a correct approaching here. One solution can be: you should put 3 WS nested and put the function call inside the last WS callback.
It also depends on how much arguments that your searchPeople need. If it only work with fully 3 arguments from WS calls, another solution is putting the function call in all 3 WS callback, and inside function searchPeople, you should add a condition statement to check if we have fully 3 argument before do searching

Why do i get undefined, even when using a promise?

I am trying to use a promise to call a function getTweets.
Not using an AJAX call, but a simple promise 'call' from 1 javascript file to another.
The function works, but i keep getting 'undefined'.
I have read dozens of questions here on stackoverflow and have spent days
to understand promises, but still can't solve it.
var Twit = require('twit') // Imports the Twitter library
require('dotenv').config() // to get the environment vars into the app
// This is the function:
function getTweets (screen_name) {
let T = new Twit({ /* <twitter key and token here> */ });
T.get('statuses/user_timeline', { screen_name: screen_name, count: 3}, function (err, data, response) {
let myTweets = [];
for (let i in data) {
let text = data[i].text;
myTweets.push(text);
}
return myTweets;
})
}
module.exports.getTweets = getTweets;
And this is the promise that tries to get the tweets:
var promise = tweets.getTweets('dizid');
promise.then(
console.log(myTweets),
console.log(err))
// gives error: promise.then(
// ^
// TypeError: Cannot read property 'then' of undefined
Any help greatly appreciated.
Your problem is that you never return anything from your getTweets() function even though it needs to return a promise. The function calls T.get() and pass it a callback function. You return from this callback function but this doesn't do anything, it doesn't mean that this value gets returned from getTweets().
This is a pretty common mistake when it comes to working with asynchronous calls. What needs to be done is to make getTweets() return a promise that gets resolved when it should.
When working with asynchronous calls that don't implement the promise interface, you need to wrap this call with a new promise. Your getTweets() function should then look like this:
function getTweets (screen_name) {
let T = new Twit({ /* <twitter key and token here> */ });
return new Promise(function(resolve, reject) {
T.get('statuses/user_timeline', { screen_name: screen_name, count: 3}, function (err, data, response) {
if (err) {
reject(err); // Reject the promise since there was an error
} else {
let myTweets = [];
for (let i in data) {
let text = data[i].text;
myTweets.push(text);
}
resolve(myTweets); // Resolve the promise with the result
}
});
});
}
However, it seems the Twit API does support the promise interface, so instead of providing a callback function you can just use the promise created by T.get(). HMR's answer explains how to do this.
Another mistake you've made is with this code:
promise.then(
console.log(myTweets),
console.log(err))
The way you've written it, it reads "Run console.log(myTweets) and console.log(err), then invoke promise.then() with the result of the former as the first argument and the result of the latter as the second argument.
then() takes callback functions (which get invoked depending on the resolving/rejection of the promise) as arguments, so the code should look like this:
promise.then(
function(myTweets) {
console.log(myTweets);
},
function(err) {
console.log(err);
});
Async/await
If you're interested in taking things further, the modern approach to working with asynchronous code is async/await, which is syntactic sugar for promises that lets you write asynchronous code more similar to regular synchronous code.
A function marked as async will implicitly return a promise, but you write it as if you return a regular value. Using the await keyword inside an async function will implicitly wait for a promise to resolve and unwrap the resolved value. The main practical benefits of this is that you can use asynchronous calls in loops and handle errors with regular try-catch blocks. Your getTweets() function would look like this using async/await:
async function getTweets(screen_name) {
let T = new Twit({ /* <twitter key and token here> */ });
const data = await T.get('statuses/user_timeline', { screen_name: screen_name, count: 3});
// Let's also use map() instead of a for loop
let myTweets = data.map(function(item) { return item.text; });
return myTweets;
}
Since get seems to return a promise you don't need to use a callback. Get Tweets can look something like this:
// in getTweets
return T.get(
'statuses/user_timeline',
{ screen_name: screen_name, count: 3}
).then(
function (data) {
console.log("data:",JSON.stringify(data,undefined,2));
return data.map(item=>item.text);
}
)
// exports the function getTweets so that other modules can use it
module.exports.getTweets = getTweets;
If that didn't work please let us know what the output of the program is (update question).
You can call getTweets like so:
tweets.getTweets('dizid')
.then(
myTweets=>
console.log(myTweets),
err=>
console.log(err)
)
I think you forget add function like
promise.then(function(res){
//code
}
Your .then() should include a call back function.
promise.then( res => {
console.log(res);
});
edit: I'm using an ES6 syntax for arrow functions, in case you're new to that.

How to wrap an async package function?

I'm using the popular node library, got, to make simple GET requests to a JSON API.
I have a function that abstracts the request, like so:
function performRequest(url) {
got(url, {
json: true
}).then(function (response) {
return formatResponse(response.body);
}).catch(function (error) {
console.log(error.response.body);
});
}
formatResponse is a simple synchronous method that modifies the JSON returned from the API.
I would like to be able to call performRequest from another function and then use the return value (once resolved). Currently, as performRequest is not recognized as an async method, my code is calling it and then proceeding immediately.
function myBigFunction() {
var url = composeUrl();
var res = performRequest(url);
doMoreStuffWithResponse(res);
}
I know that I need to utilize a Promise, however, I'm always unclear as to how to use a Promise in conjunction with a built-in library function that is already using a Promise (like in this case).
I'm also completely open to the possibility that I'm going about this all wrong. In that case, I would appreciate some redirection.
Thank you for your time.
Understand what a Promise is. Its a value, you can treat it as such. In order to "read" the value, you pass a function to the Promise's then method. You don't need myBigFunction. Anything you want to run after the Promise resolves just needs to be passed to then:
var req = performRequest(composeURL());
req.then(doStuffWithResponse);
Now, I don't particularly care for this way although I do it fairly often. I prefer to have functions that take promises and invoke their then method:
var takesAPromise = function(p) {
return p.then(/* does stuff */);
};
Note that it returns the Promise of the completed task. But what I like even better is this ES6 one-liner:
let wrap = f => p => p.then(val => f.call(null, val));
Now you can wrap arbitrary functions to take Promises as input and return them as output. If Promises were a monad, this would be their bind function. Making it work seamlessly with functions of arbitrary arity is left as an exercise to the reader.
You'll always want to return a promise from your functions:
function performRequest(url) {
return got(url, {
//^^^^^^
json: true
}).then(function(response) {
return formatResponse(response.body);
}, function(error) {
throw new Error(error.response.body);
});
}
With this, you can wait for the result in your big functions using another then:
function myBigFunction() {
var url = composeUrl();
var promise = performRequest(url);
return promise.then(function(res) {
return doMoreStuffWithResponse(res);
});
}
or in short
function myBigFunction() {
return performRequest(composeUrl()).then(doMoreStuffWithResponse);
}
so that you can call it like
myBigFunction().catch(function(error) {
console.log(error.message);
});

Can promises be relayed in node

I have the following requirement,I have three asynchronous function async1(),async2(),ascync3() which all return promises
Now I would call one function and serially execute async1,async2,async3 respectively and I want to print the resolved promise returned after async3
this is my main function
testPromiseSerially = function() {
return new promise(function(resolve,reject) {
async1().
then(function(result1) {
return async2(result1)
})
.then(function(result2){
return async3(result2)
})
.catch(function(err) {
return reject(err)
}
})
}
This is my async3 function
async3 = function(params) {
return new promise(function(resolve,reject) {
return resolve("solved")
})
}
and async1 and async2 are also similar to async3
If I execute this code
testPromiseSerially.then(function(result) {
console.log(result)
})
.catch(function (err) {
console.log(err)
})
testPromiseSerially is getting called but it's not entering 'then' or 'catch'
block.Is promise returned by async3 not relayed back to testpromiseSerially()?
How do I see the result from async3?
I know that if I extend my code like adding
.then(function(result) {
return resolve(result)
})
after async3(result) then I would be able to see the result. But I have chain of functions which depend on promise returned by other functions, so how do I handle that?
The main problem is that your testPromiseSerially code is never calling resolve. So the promise it returns is never resolved.
Since what it's testing already has promises, there's no need for you to create a new one. Every call to then produces a new promise, so just use that.
Additionally, this:
.then(function(result1) {
return async2(result1);
})
is more complicated/verbose than you need, it can be just:
.then(async2)
And the same for the .catch.
So:
let testPromiseSerially = function() {
return async1()
.then(async2)
.then(async3);
};
Example using JavaScript's native promises on Babel's REPL
You should use async https://github.com/caolan/async. It is be better for your case. Look at the waterfall function.
From the documentation
waterfall(tasks, [callback])
Runs the tasks array of functions in series, each passing their results to the next in the array. However, if any of the tasks pass an error to their own callback, the next function is not executed, and the main callback is immediately called with the error.

Categories