I have the following sample code,
class GetResultsFromDatabase{
results : SiteTable[];
db : AccessDatabase;
constructor(){
this.db = new AccessDatabase();
}
getAllLevelsSites(){
this.db.getSitesDb(this.fullFillSites);
}
private fullFillSites(data : SiteTable[]){
this.results= data;
this.db.getUrlsDb(this.fullFillUrls);
}
private fullFillUrls(data : UrlsTable[]){
data.map( (current) => this.results[this.results.findIndex(obj => obj.id =
current.token)].urlTable = current );
}
}
If some code outside class calls the method "getAllLevelsSite" i want to return the complete results array with all the values fullfilled (after the last "fullFillUrls function complete).
The "db" object uses the mysql library so all the methods works only with callbacks.Which options do i have ? Is it possible to create any kind of Promise in getAllLevelsSite method in a way that the code that is calling it can use async await syntax ? Or the only way is to pass one callback function to the method getAllLevelsSite ?
Thanks in advance.
Best regards.
Your example does not provide impl. for getSitesDb so will guess how the callback looks like...
getAllLevelsSites() {
return new Promise((resolve, reject) => {
this.db.getSitesDb(this.fullFillSites, (arr, err) => {
if (err) {
reject(err);
} else {
resolve(arr);
}
});
});
}
Now wherever you are consuming this method you can use await
Untested (so I could be wrong), but rewriting getAllLevelsSites method to something like the below might work:
getAllLevelsSites(): Promise<SiteTable[]> {
return new Promise((resolve) => {
this.db.getSitesDb(resolve);
});
}
Based on the signature of fullFillSites, I've assumed that the callback passed to this.db.getSitesDb will be invoked with at least one argument (of type SiteTable[]).
Since getAllLevelsSites returns in a promise (in this code above), you should be able to then use async/await syntax.
Unrelated: You use Array.prototype.map to iterate over data in fullFillUrls, but don't do anything with the array that map returns. Seems like you should swap map out for forEach.
Related
I have async function that returns true or false. It looks like following one:
class User {
async canManageGroup(group) {
if (typeof group === 'number') {
// group - id
group = await getGroupById(group)
} // else group is already loaded from DB
return this.id === group.manager.id
}
}
If group parameter is ID of group then function will make async call to DB, so function canManageGroup will execute asynchronously. But if group parameter is group model then function will only call return this.id === group.manager.id or it will execute syncronously. Is it good practice to write code in this way? Or I should transform synchronous code to asynchronous?
function makeAsync(cb) {
return new Promise(resolve => setImmediate(() => resolve(cb())))
}
class User {
async canManageGroup(group) {
if (typeof group === 'number') {
// group - id
group = await getGroupById(group)
} // else group is already loaded from DB
return await makeAsync(() => this.id === group.manager.id)
}
}
You can use the first example without problems.
When you use async, your function will return a Promise. If your code is sync, the status of the returned promise will be resolved and it is safe to run any promise-related code on it (then, catch, etc).
For example:
async function truePromise() {
return true;
}
truePromise().then(function(value){
console.log("Promise value:", value);
});
Just works :)
Should you do it?
Yes. It is okay and works okay thanks to the async keyword.
What you MUST NOT do is the following:
function dontDoIt(doSync) {
if (doSync) return false;
return Promise.resolve(false)
}
Why? Because:
if doSync is truhtly, it will return false (i.e a boolean)
if doSync is falsy, it will return a Promise that will resolve to false.
That is a HUGE difference.
Why?
Your function sometimes returns a promise, and other times return a boolean. It is inconsistent.
doSync(true) is a boolean, you can't use await nor .then.
doSync(false) is a Promise, you can use await and then.
Based on your comment to one of the answers you are concerned because of the answer to the question How do you create custom asynchronous functions in node.js?
The problem is, this function is inconsistent: sometimes it is asynchronous, sometimes it isn't. Suppose you have a consumer like this:
In js enviroments like nodejs is common practice that the callback that could be executed async, should always be called async, no matter if the actual code was really async.
So you are wondering if the following code will break that common practie.
async function doSomething() {
return true
}
doSomething()
.then(res => {
console.log(res)
})
This is not the case, because here you deal with Promises and a Promise is allowed to be resolve immediatly, the code above is basicly the same as if you would write:
Promise.resolve(true)
.then(res => {
console.log(res)
})
The async behaviour is ensured in the chaining part (then/catch), the callback you pass to the then/catch will be called async:
async function doSomething() {
return true
}
console.log('before');
doSomething()
.then(res => {
console.log('with in callback')
})
console.log('after');
As you can see the order of the logs is:
before
after
with in callback
So this is in the same order you would expect from an regular async callback function.
The first one is correct. There is no need to force anything to async if there doesn't need to be. Context switching isn't free, so if it doesn't have to wait for anything to complete, don't try and make it.
I have some code that looks like
//service.ts
addProduct(productId) {
this.http.post('someUrl', ReqData).map(json).subscribe(doStuff);
}
//component.ts
addAllproducts(productsIds) {
productIds.forEach(productId => service.addProduct(productId);
}
What I want is to be able to wait for each call to finish before calling for the next productId, without using window.setTimeout ..
How about some recursive calls using .expand()?
First, create a recursive function and map the data for recursive use:
const recursiveAddProduct = (currentProductId, index, arr)=>{
return service.addProduct(currentProductId)
.map((response)=>{
return {
data:response,
index: index+1,
arr:arr
}
})
};
Now, call it recursively in your component:
//productIds is an array of Ids
//start of using the first index of item, where index = 0
let reduced = recursiveAddProduct(productIds[0],0,productIds)
.expand((res)=>{
return res.index>res.arr.length-1 ? Observable.empty(): recursiveAddProduct(productIds[res.index],res.index,productIds)
});
reduced.subscribe(x=>console.log(x));
Here is a working JSBin
Benefit of using .expand operator:
You are still using Observables and can chain whatever operators you want to.
You are calling one http after another, which is your requirement.
You don't need to worry about error handling, they are all chained to a single stream. Just call a .catch to your observables.
You can do anything to your recursion method (data manipulation,etc)
You can set the condition when to terminate the recursion call.
One-liner (almost) code.
Edit
You can use .take() operator to terminate your recursion, if you don't like the inline ternary, like this:
let reduced = recursiveAddProduct(productIds[0],0,productIds)
.expand(res=>recursiveAddProduct(productIds[res.index],res.index,productIds))
.take(productIds.length)
Working JSBin
First return the observable from your service method:
addProduct(productId) {
return this.http.post('someUrl', ReqData).map(json).subscribe(doStuff);
}
And use a recursive function and call it in the subscribe callback for each of the items in your array:
let loop = (id: number) => {
service.addProduct(id)
.subscribe((result) => {
// This logic can be modified to any way you want if you don't want to mutate the `producIds` array
if (productIds.length) {
loop(productIds.shift())
}
})
}
loop(productIds.shift())
You can use the Observable.merge().
Try something like that
addProduct(productId):Observable<Response> {
return this.http.post('someUrl', productId);
}
addAllproducts(productsIds) {
let productedsObservable:Observable<Response>[]=[];
for(let productID in productsIds){
productedsObservable.push(this.addProduct(productID));
}
return merge(productedsObservable)
}
You need to subscribe to the requested function for it the execute the HTTP request.
You can read more about combination operators (like merge) here
I saw promise implementation in Handling multiple catches in promise chain which produce a very readable chain
return validateInput
.then(checkLoginPermission)
.then(checkDisableUser)
.then(changePassword);
However, in order to do this each function needs to return a value instead of a Promise? Since Promise can resolves to either value or a Promise so this is not a problem. My goal is to turn every function to have readable clear logic as such.
The problem occurs when trying to unwind the nested promise function
return validateInput
.then(function(resultA) {
return checkLoginPermission
.then (function(resultB) {
// Do something with resultA
})
});
Imagine the original implementation involves accessing the value from previous promise. With nested promise, it is easily achievable. But with flatten chain, I would need to break up each function like this
function validateInput = function (resultA ) {
return Promise.resolve({resultA : resultA, resultB :
}
function checkLoginPermission = function (mix ) {
let resultA = mix.resultA;
let resultB = mix.resultB
//Do something with resultA
...
}
This is worse when the last function in the chain rely on something from the very beginning. That means the value have to be passed down from the beginning of the chain even if it was not used.
So am I accidentally stepping on some kind of anti-pattern that might affect performance? How else can I achieve good readability without all these hassles?
This is actually where async and await come in. It's good when you need results across multiple asynchronous calls/promises to be in scope. If you can use that, I'd say try it.
async function foo () {
const input = await validateInput()
const hasPermission = await checkLoginPermission(input)
const result = await checkDisableUser(hasPermission)
return await changePassword(result)
}
Just pass the variables into what function as they need to be. Just showing an example there. I was also a bit unsure of how you're setting validateInput, i think you need to put await infront of the function call itself.
If you cannot use async/await, I usually go with your 2nd code snippet, or define the higher scope variables ontop:
let resultA
return validateInput
.then(function(result) {
resultA = result
return checkLoginPermission
.then (function(resultB) {
// Do something with resultA
})
});
Promises are pattern, related to functional programming, there direct passing data from one function to other is a basic (it's called compose, here examples: http://scott.sauyet.com/Javascript/Talk/Compose/2013-05-22/). So it's not anti-pattern by no means.
I can't see any problem in such pattern. You can pass any data you want to next Promises and in nested Promises grab what they need. It's preety transparent and clear:
function validateInput() {
return Promise.resolve({resultA: 1});
}
function checkLoginPermission(result) {
return new Promise(function(resolve, reject) {
// ...
// code
// ...
result.resultB = 2;
return resolve(result);
});
}
function checkDisableUser(result) {
return new Promise(function(resolve, reject) {
// grab some data from previous function
let resultB = result.resultB;
// ...
// code
// ...
result.resultC = 3;
return resolve(result);
});
}
function changePassword(result) {
return new Promise(function(resolve, reject) {
// grab some data from previous functions
let resultB = result.resultB;
let resultC = result.resultC;
// ...
// code
// ...
result.resultD = resultB * resultC;
return resolve(result);
});
}
validateInput()
.then(checkLoginPermission)
.then(checkDisableUser)
.then(changePassword);
Also you can collect data in some variable, declared before Promises and so you will not have to pass result. But it will destroy functional nature of Promises.
The inner .then(/* ... */) callbacks can return either a primitive value or a Promise that resolves to some value. If it is another promise then the next .then won't start until the inner promise is resolved. Essentially, Promises always resolve to a non-promise type. If you resolve or return another Promise, it will be automatically unwrapped.
I would like to propose a solution using ramda.js#pipeP().
The good thing about this function is that it resolves promises sequentially.
We can rewrite your example using pipeP():
import pipeP from 'ramda/src/pipeP'
pipeP([
checkLoginPermission,
checkDisableUser,
changePassword
])(initialValue)
.then(responseChangePassword => { ... })
The results of a previous promise are passed to the following one.
I'm trying to add a "default callback" to a prototype that will assign a callback function (in the form of a promise) to an async method if none is provided.
The goal is to have a class's chain of async methods run synchronously
Item.async1().async2()....asyncN()
Mind you, the async functions themselves expect a callback but they're not passed as arguments in the function call (which tells me that the class needs a default behavior when the callback lookup fails)
Spec states that I cannot directly modify the behavior or side effects of the prototype methods. I can add prototype methods. We have no visibility into how these prototype methods are implemented.
TLDR: Without modifying the prototype methods, how can you chain an N number of async methods and ensure they run sequentially?
BTW: Promisifying the prototype methods in question would be helpful if I wanted to implement the promisified versions, but it looks like we're constrained to the original function calls
Well, I wasn't going to answer - but I was challenged.
It's quite easy to utilize the built in abilities of promises in order to get this sort of queuing for free. Here is how this conversion will work:
We convert the callback API to promises on a new object with a promise subclass.
We add all the promised methods to the subclass itself - so it chains.
We tell the subclass to execute all the methods in a then, so they'll queue as then is the queueing mechanism promises have.
Note: The promisify and promisifyAll methods I write here - you should grab off NPM - lots of good and fast usages that take a promise constructor.
First, we need a method that converts a callback API to promises:
// F is a promise subclass
function promisify(fn) { // take a function
return function(...args) { // return a new one with promises
return new F((resolve, reject) => { // that returns a promise
// that calls the original function and resolves the promise
fn.call(this, ...args, (err, data) => err ? reject(err) : resolve(data));
});
};
}
Now, let's promisify the whole object:
function promisifyAll(obj) {
const o = {};
for(const prop in obj) {
if(!(obj[prop].call)) continue; // only functions
o[prop] = promisify(obj[prop]).bind(obj);
}
return o;
}
So far, nothing new, lots of NPM libraries do this - now for the magic of promises - let's create a method that executes functions on an original object in a then:
function whenReadyAll(obj) {
const obj2 = {}; // create a new object
for(const prop in obj) { // for each original object
obj2[prop] = function(...args) {
// return a function that does the same thing in a `then`
return this.then(() => obj[prop](...args));
};
}
return obj2;
}
Now, let's wrap things up
function liquidate(obj) {
const promised = promisifyAll(obj); // convert the object to a promise API
class F extends Promise {} // create a promise subclass
Object.assign(F.prototype, whenReadyAll(promised)); // add the API to it
return promised; // return it
// previous code here
}
And that's it, if we want the example to be self contained (again, promise and promisifyAll are provided by a library usually):
function liquidate(obj) {
const promised = promisifyAll(obj);
class F extends Promise {}
Object.assign(F.prototype, whenReadyAll(promised)); // add the API
return promised;
function whenReadyAll(obj) {
const obj2 = {};
for(const prop in obj) {
obj2[prop] = function(...args) {
return this.then(() => obj[prop](...args));
};
}
return obj2;
}
function promisifyAll(obj) {
const o = {};
for(const prop in obj) {
if(!(obj[prop].call)) continue; // only functions
o[prop] = promisify(obj[prop]).bind(obj);
}
return o;
}
function promisify(fn) {
return function(...args) {
return new F((resolve, reject) => {
fn.call(this, ...args, (err, data) => err ? reject(err) : resolve(data));
});
};
}
}
Or with a library that does promisify:
function liquidate(obj) { // 14 LoC
class F extends Promise {}
const promised = promisifyAll(obj, F); // F is the promise impl
Object.assign(F.prototype, whenReadyAll(promised)); // add the API
return promised;
function whenReadyAll(obj) {
const obj2 = {};
for(const prop in obj) {
obj2[prop] = function(...args) {
return this.then(() => obj[prop](...args));
};
}
return obj2;
}
}
And what's an answer without a demo:
var o = { // object with a delay callback method
delay(cb) {
console.log("delay");
setTimeout(() => cb(null), 1000);
}
};
var o2 = liquidate(o); // let's liquidate it
// and we even get `then` for free, so we can verify this works
var p = o2.delay().then(x => console.log("First Delay!")).
delay().
then(x => console.log("Second Delay!"));
// logs delay, then First Delay! after a second,
// then delay and then Second Delay! after a second
Copy paste this to your friendly neighborhood console and see for yourself :)
To prove this preserves state on the original object (it's easy to modify it not to if that's a requirement) let's add an i variable and increment it in delay and see that things work:
var o = { // object with a delay callback method
delay(cb) {
console.log("delay", this.i++);
setTimeout(() => cb(null), 1000);
},
i: 0
};
var o2 = liquidate(o); // let's liquidate it
// and we even get `then` for free, so we can verify this works
var p = o2.delay().then(x => console.log("First Delay!")).
delay().
then(x => console.log("Second Delay!", o.i));
//logs:
// delay 0
// First Delay!
// delay 1
// Second Delay! 2
If .async1() and .async2() are already provided and they require a callback and you aren't allowed to modify them, then you can't achieve Item.async1().async2()....asyncN(). The methods you're calling just aren't built to work that way and if you're not allowed to change them, there's not much you can do other than replace those methods with methods that do work the way you want.
If you can create new methods with their own names that internally use the original methods, then that can be done. One model for how to do that is jQuery animations. They allow you do things like this:
$("#progress").slideDown(300).delay(1000).slideUp(300);
And each of those async operations will be chained together. jQuery accomplishes that by doing the following:
Each method returns the original object so chaining of any method on the object will work.
If an async operation is already running, then each new async method that gets called goes into a queue (on the object) along with the arguments for that method.
The underlying implementation of each method can use an async operation with a traditional callback (or promise). When the operation is done, it then checks the queue to see if there are more operations to be run and if so, starts the next operation form the queue and removes it from the queue.
If new async operations are called while another is running, they again just get added to the end of the queue. Each object (or whatever group of items that you want to be serialized) has its own queue.
So, if the original async methods that expect callbacks were .async1() and .async2(), you could create .async1Chain() and .async2Chain() such that you could make it work like this:
Item.async1Chain().async2Chain()....asyncNChain()
Where internally, .async1Chain() would call .async1() with a local callback and that callback would be configured to check the queue to run the next queued operation if there was one.
This is just one method of solving the problem. There are likely others.
I whould suggest you to use a library for that, i made one myself that allow not only secuential chaining but let you use loops and ifElse structures.
https://github.com/Raising/PromiseChain
(note that we are not using a parent object in this example, that cleans the code a lot)
The internal scope save the result of each continue with the name provided
var internalScope = {}; //i'll use scope
new PromiseChain(internalScope )
.continue(function(internalScope){ return async1();},"firstResult")
.continue(function(internalScope){ return async2();},"secondResult")
.continue(function(internalScope){ return async3();},"thridResult")
.end();
Alternatively if all functions belong to the same object and only need the scope as parameter you can do this
new PromiseChain(internalScope,yourObject) // this is important if you use the 'this' keyword inside the functions, it works as a .bind(yourObject) for every function
.continue(yourObject.async1,"firstResult")
.continue(yourObject.async2,"secondResult")
.continue(yourObject.async3,"thridResult")
.end();
A Bluebird Promise returns an object that contains two arrays of objects, cars and contracts. Then, I want to iterate over the cars, call an asynchronous function and, based on the returned value, make some changes to the second array and return the initial result object with these changes. I can't figure out how to do this with promises. Or with async, for that matter. I feel like they should be nested promises, but i can;t get it to work at all.
the version with promises:
somePromise().then(function (result) {
Promise.each(result.cars, function (car) {
makeAsyncCall(car.id, function (err, resultArray) {
if (err) {
throw new Error();
}
result.contracts.forEach(function (contract) {
if (resultArray.indexOf(contract.id) > -1) {
contract.car = car.id;
}
});
});
}).then(function (eachResult) {
//eachResult is result.firstArray, which is not interesting.
return result;
});
}).then(function (result)) {
//this gets called before my promise.each gets executed??
}
Can anyone give me a hint as to where my mistake is?
Have a look at my rules of thumb for promise development. The two specific points that apply to your code are:
promisify your async callback-taking functions before using them, specifically
var makeCall = Promise.promisify(makeAsyncCall);
always return promises from your functions that do asynchronous things. This is especially true for callbacks, like the function() { Promise.each(…).then(…) } and the function() { makeAsyncCall(…) }.
With those, you should get to the following:
somePromise().then(function(result) {
return Promise.each(result.cars, function(car) {
return makeCall(car.id).then(function(resultArray) {
// a lookup structure of contracts by id could make this more efficient
result.contracts.forEach(function (contract) {
if (resultArray.indexOf(contract.id) > -1)
contract.car = car.id;
});
});
}).return(result);
}).…