I have the following code in my testUtils.js:
var truncateTable = function (model, trx) {
console.log(`Truncating ${model.prototype.tableName}`);
return knex(model.prototype.tableName).transacting(trx).truncate();
};
utils = {
/**
* Database utility for cleaning up its tables
*/
teardown: function () {
return knex.transaction(function (trx) {
return knex.raw('SET foreign_key_checks = 0;').transacting(trx)
.then(() => {
return truncateTable(models.ProductAnomaly,trx)
})
.then(() => {
return truncateTable(models.Product, trx);
})
.then(() => {
return truncateTable(models.TaskListItem, trx);
})
.then(() => {
return truncateTable(models.TaskList, trx);
})
.then(() => {
return truncateTable(models.User, trx);
})
.then(() => {
console.log('TRUNCATE FINISHED');
return knex.raw('SET foreign_key_checks = 1;').transacting(trx);
});
});
}
}
When I run my test it prints:
Truncating products_anomalies
Truncating products
Truncating tasklist_items
Truncating tasklists
Truncating users
But as you may see the line "TRUNCATE FINISHED" is never printed, even though the action (truncate) is performed for all 5 tables. Why?
Here's the before method in my test file:
before((done) => {
this.timeout(10000);
testUtils.teardown()
.then(() => {
return testUtils.setup();
})
.then(() => {
done();
})
.catch(err => console.log(JSON.stringify(err)));
})
Most likely because you don't have a .catch in your teardown function, so if there was an error with your last truncateTable call, you would never know without a catch rejecting the promise.
Try:
.then(() => {
return truncateTable(models.User, trx);
})
.then(() => {
console.log('TRUNCATE FINISHED');
return knex.raw('SET foreign_key_checks = 1;').transacting(trx);
})
.catch((err) => {
throw err;
});
I found what was stopping the execution, the sequence of truncates and inclusion of sample data was taking more than 2000ms, then I had to increase the timeout value.
Related
In the code below, I am conditionally switching to another observable. If the condition is met it works fine. If the condition is not met and I return EMPTY from switchMap, the code in the subscribe block is not executed.
If I change return EMPTY to return of(x) it works.
this.claimStoreService.setProducts(this.claim.products)
.pipe(switchMap(x => {
if (this.incomeEligibility) {
return this.claimStoreService.saveIncomeEligibility();
} else {
return EMPTY;
}
}))
.subscribe(() => {
this.isSaving = false;
this.goIntoDisplayMode();
}, () => {
this.isSaving = false;
});
Try to use the third callback:
this.claimStoreService.setProducts(this.claim.products)
.pipe(switchMap(x => {
if (this.incomeEligibility) {
return this.claimStoreService.saveIncomeEligibility();
} else {
return EMPTY;
}
}))
.subscribe(
() => this.goIntoDisplayMode(),
console.err,
() => this.isSaving = false,
);
It is probably more clear if you pass to the subscribe function an Observer. Your code would become
this.claimStoreService.setProducts(this.claim.products)
.pipe(switchMap(x => {
if (this.incomeEligibility) {
return this.claimStoreService.saveIncomeEligibility();
} else {
return EMPTY;
}
}))
.subscribe({
next: () => this.goIntoDisplayMode(),
error: err => console.error(err),
complete: () => this.isSaving = false,
});
I'm trying to create an Observable from an array of items that each regularly check for server updates and then sends an action when it gets the result it wants for each item.
The answer below is helpful, however not quite what I'm looking for
This is the other approach I've been trying:
export function handleProcessingScenes(action$,store) {
return action$.ofType(REQUEST_ALL_SCENES_BY_LOCATION_FULFILLED)
.switchMap(({ scenesByLocation }) => Observable.from(scenesByLocation))
.filter(scene => scene.scenePanoTask)
.mergeMap(scene => updateScene(scene))
}
function updateScene(scene) {
return Observable.interval(3000)
.flatMap(() => requestSceneUpdates(scene.id))
.takeWhile(res => res.payload.status < 4)
.timeout(600000, Observable.throw(new Error('Timeout')))
}
The API function returns an Observable
export function requestSceneUpdates(sceneId){
console.log('requestSceneUpdate')
const request = fetch(`${API_URL}/scene/task/${sceneId}/update`, {
method: 'get',
credentials: 'include',
crossDomain: true,
}).then(res => res.json())
return Observable.fromPromise(request)
}
However this only calls the 'requestSceneUpdate' function once.
I basically want to call that function every 3 seconds for each scene in scenesByLocation. I then want to return an action when each one is finished.
The epic that I have for a single scene is
export function sceneProcessingUpdate(action$) {
return action$.ofType(REQUEST_SCENE_PROCESSING_TASK_SUCCESS)
.switchMap(({task}) =>
Observable.timer(0, 30000).takeUntil(action$.ofType( REQUEST_SCENE_PROCESSING_TASK_UPDATE_SUCCESS))
.exhaustMap(() =>
requestSceneUpdates(task.id)
.map((res) => {
if (res.error)
return { type: REQUEST_SCENE_PROCESSING_TASK_UPDATE_FAILED, message: res.message }
else if(res.payload.status === 4)
return { type: REQUEST_SCENE_PROCESSING_TASK_UPDATE_SUCCESS, task: res.payload }
else
return requestSceneProcessingTaskMessage(res.payload)
})
.catch(err => { return { type: REQUEST_SCENE_PROCESSING_TASK_UPDATE_FAILED, message: err } })
)
)
}
I think you need something like this. The idea is to retry the scene update if it fails, after 3 seconds and not use a timer.
export function handleProcessingScenes(action$) {
return action$.ofType(REQUEST_ALL_SCENES_BY_LOCATION_FULFILLED)
.switchMap(({ scenesByLocation }) => Observable.from(scenesByLocation))
.filter(scene => scene.scenePanoTask)
.mergeMap(scene => updateScene(scene));
}
function updateScene(scene) {
return requestSceneUpdates(scene.id)
.map((res) => {
if (res.error)
throw res.error;
else if (res.payload.status === 4)
return { type: REQUEST_SCENE_PROCESSING_TASK_UPDATE_SUCCESS, task: res.payload }
else
return requestSceneProcessingTaskMessage(res.payload)
})
.retryWhen(errors => errors.delay(3000));
}
This worked in the end, #Andrew fixed the first part.
export function handleProcessingScenes(action$,store) {
return action$.ofType(REQUEST_ALL_SCENES_BY_LOCATION_FULFILLED)
.switchMap(({ scenesByLocation }) => Observable.from(scenesByLocation))
.filter(scene => scene.scenePanoTask)
.flatMap(scene => {
return Observable.timer(0, 5000).takeUntil(action$.ofType( REQUEST_SCENE_PROCESSING_TASK_UPDATE_SUCCESS))
.exhaustMap(() =>
requestSceneUpdates(scene.id)
.map((res) => {
if (res.error)
return { type: REQUEST_SCENE_PROCESSING_TASK_UPDATE_FAILED, message: res.message }
else if(res.payload.status === 4)
return { type: REQUEST_SCENE_PROCESSING_TASK_UPDATE_SUCCESS, task: res.payload }
else
return requestSceneProcessingTaskMessage(res.payload)
})
.catch(err => { return { type: REQUEST_SCENE_PROCESSING_TASK_UPDATE_FAILED, message: err } })
)
})
}
I saw a similar question here which doesnt solve my problem. I am trying to run a cron job every 10 hours that lets me get the categories first and then based on the categories, i find the information for each category. How can I simplify the below Promise. I am NOT using Bluebird or Q, this is the native JS promise. Honestly, the code below looks like the same callback hell Promises were supposed to avoid, any suggestions
flipkart.getAllOffers = function () {
interval(43200, () => {
flipkart.findAllCategories()
.then((categories) => {
flipkart.save('flipkart_categories.json', categories)
if (categories) {
for (let item of categories) {
flipkart.findAllForCategory(item.category, item.top)
.then((items) => {
flipkart.save('flipkart_top_' + item.category + '.json', items)
}).catch((error) => {
console.log(error)
})
}
}
})
.catch((error) => {
console.log(error)
})
})
}
function interval(seconds, callback) {
callback();
return setInterval(callback, seconds * 1000);
}
If you stop using an extra level of indent just for .then(), then you have a pretty simple structure.
One .then() handler that contains
an if() statement
that contains a for loop
that contains another async operation
In this modified version, half your indent comes from your if and for which has nothing to do with promises. The rest seems very logical to me and doesn't at all look like callback hell. It's what is required to implement the logic you show.
flipkart.getAllOffers = function () {
interval(43200, () => {
flipkart.findAllCategories().then((categories) => {
flipkart.save('flipkart_categories.json', categories)
if (categories) {
for (let item of categories) {
flipkart.findAllForCategory(item.category, item.top).then((items) => {
flipkart.save('flipkart_top_' + item.category + '.json', items)
}).catch((error) => {
console.log(error)
throw error; // don't eat error, rethrow it after logging
});
}
}
}).catch((error) => {
console.log(error)
})
})
}
If flipkart.save() is also async and returns a promise, then you probably want to hook those into the promise chain too.
You can always create a helper function that may improve the look also like this:
flipkart.getAllOffers = function () {
interval(43200, () => {
flipkart.findAllCategories().then(iterateCategories).catch((error) => {
console.log(error);
})
})
}
function iterateCategories(categories) {
flipkart.save('flipkart_categories.json', categories);
if (categories) {
for (let item of categories) {
flipkart.findAllForCategory(item.category, item.top).then((items) => {
flipkart.save('flipkart_top_' + item.category + '.json', items);
}).catch((error) => {
console.log(error);
});
}
}
}
If you're trying to collect all the results (something your title implies, but your question doesn't actually mention), then you can do this:
flipkart.getAllOffers = function () {
interval(43200, () => {
flipkart.findAllCategories().then(iterateCategories).then((results) => {
// all results here
}).catch((error) => {
console.log(error);
});
})
}
function iterateCategories(categories) {
flipkart.save('flipkart_categories.json', categories);
let promises = [];
if (categories) {
for (let item of categories) {
let p = flipkart.findAllForCategory(item.category, item.top).then((items) => {
flipkart.save('flipkart_top_' + item.category + '.json', items);
}).catch((error) => {
console.log(error);
});
promises.push(p);
}
}
// return promise here that collects all the other promises
return Promise.all(promises);
}
I have a method which returns a promise like:
checkLogin(credentials) {
return new Promise((resolve, reject) => {
this.http.post(url, credentials)
.map(res => res.json())
.subscribe(
data => {
resolve(data);
},
err => {
reject(err);
}
);
});
}
I call this method inside another:
login(credentials) {
this.checkLogin(credentials)
.then(function(result) {
console.log("ok: ",result);
this.doAlert("ok");
})
.catch(function(err) {
console.log("error: ",err.message);
this.doAlert(err.message)
});
}
Here is where the error happens, it is said "TypeError: this.doAlert is not a function":
But the doAlert is in the same file than the others, and it works fine from other places (not promises calls)
doAlert(text) {
let alert = Alert.create({
title: 'Alert;,
subTitle: text,
buttons: ['Ok']
});
this.nav.present(alert);
}
Is it not possible to do this?
Use fat-arrow functions instead
login(credentials) {
this.checkLogin(credentials)
.then((result) => {
console.log("ok: ",result);
this.doAlert("ok");
})
.catch((err) => {
console.log("error: ",err.message);
this.doAlert(err.message)
});
}
to keep the scope
See also
- https://basarat.gitbooks.io/typescript/content/docs/arrow-functions.html
- https://github.com/Microsoft/TypeScript/wiki/'this'-in-TypeScript
- What's the meaning of "=>" in TypeScript? (Fat Arrow)
Use arrow functions
login(credentials) {
this.checkLogin(credentials)
.then((result) => {
console.log("ok: ",result);
this.doAlert("ok");
})
.catch((err) => {
console.log("error: ",err.message);
this.doAlert(err.message)
});
I'm trying to chain promises, but the second one doesn't call the resolve function. What do I do wrong?
function getCustomers(){
let promise = new Promise((resolve, reject) => {
console.log("Getting customers");
// Emulate an async server call here
setTimeout(() => {
var success = true;
if (success) {
resolve( "John Smith"); // got the customer
} else {
reject("Can't get customers");
}
}, 1000);
}
);
return promise;
}
function getOrders(customer) {
let promise = new Promise((resolve, reject) => {
console.log("Getting orders");
// Emulate an async server call here
setTimeout(() => {
var success = true;
if (success) {
resolve("Order 123"); // got the order
} else {
reject("Can't get orders");
}
}, 1000);
}
);
return promise;
}
getCustomers()
.then((cust) => getOrders(cust))
.catch((err) => console.log(err));
console.log("Chained getCustomers and getOrders. Waiting for results");
The code prints "Getting orders" from the second function, but doesn't print "Order 123":
Getting customers
Chained getCustomers and getOrders. Waiting for results
Getting orders
Update. I wanted to insert the print on the console between chained methods that return promises. I guess something like this is not possible:
getCustomers()
.then((cust) => console.log(cust)) //Can't print between chained promises?
.then((cust) => getOrders(cust))
.then((order) => console.log(order))
.catch((err) => console.error(err));
You want to chain a success handler (for your resolve result "Order 123"), not an error handler. So use then instead of catch :-)
getCustomers()
.then(getOrders)
.then((orders) => console.log(orders))
.catch((err) => console.error(err));
None of the promises was rejected, so the console.log(err) in your code was never called.
I wanted to insert the print on the console between chained methods that return promises. I guess something like this is not possible:
getCustomers()
.then((cust) => console.log(cust)) //Can't print between chained promises?
.then((cust) => getOrders(cust))
Yes it is possible, but you are intercepting a chain here. So the second then callback actually is not called with cust, but with the result of the first then callback - and console.log returns undefined, with which getOrders will get some problems.
You'd either do
var customers = getCustomers();
customers.then(console.log);
customers.then(getOrders).then((orders) => …)
or simpler just
getCustomers()
.then((cust) => { console.log(cust); return cust; })
.then(getOrders)
.then((orders) => …)
Here is a code example for Sequential execution for node.js using ES6 ECMAScript. Maybe somebody finds it useful.
http://es6-features.org/#PromiseUsage
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
var soapClient = easysoap.createClient(params);
//Sequential execution for node.js using ES6 ECMAScript
console.log('getAllFunctions:');
soapClient.getAllFunctions()
.then((functionArray) => {
return new Promise((resolve, reject) => {
console.log(functionArray);
console.log('getMethodParamsByName:');
resolve();
});
})
.then(() => {
return soapClient.getMethodParamsByName('test1'); //will return promise
})
.then((methodParams) => {
console.log(methodParams.request); //Console log can be outside Promise like here too
console.log(methodParams.response);
console.log('call');
return soapClient.call({ //Return promise
method: 'test1',
params: {
myArg1: 'aa',
myArg2: 'bb'
}
});
})
.then((callResponse) => {
console.log(callResponse); // response data as json
console.log('end');
})
.catch((err) => {
throw new Error(err);
});