recently I started using generators in my angular project. Here's how I do it so far:
function loadPosts(skip) {
return $rootScope.spawn(function *() {
try {
let promise = yield User.findAll();
$timeout(function () {
// handle the user list
});
} catch (err) {
// handle err
}
});
}
From what I've read the next part won't be necessary in es7, but currently I have the spawn function in the run block of my app.
$rootScope.spawn = function (generatorFunc) {
function continuer(verb, arg) {
var result;
try {
result = generator[verb](arg);
} catch (err) {
return Promise.reject(err);
}
if (result.done) {
return result.value;
} else {
return Promise.resolve(result.value).then(onFulfilled, onRejected);
}
}
var generator = generatorFunc();
var onFulfilled = continuer.bind(continuer, "next");
var onRejected = continuer.bind(continuer, "throw");
return onFulfilled();
};
Everything works find the way I do it at the moment, the only thing I really don't like is that I have to call $timeout() after each promise. If I don't my $scope variables initialized inside the timeout won't be initialized. It seems to me that angular digest system needs to be triggered manually.
Why is that and is there a way to make this cleaner?
I would assume it is because your spawn method uses native Promises, not the angular implementation. Try to use $q instead:
function continuer(verb, arg) {
var result;
try {
result = generator[verb](arg);
} catch (err) {
return $q.reject(err);
}
if (result.done) {
return result.value;
} else {
return $q.resolve(result.value).then(onFulfilled, onRejected);
}
}
Related
I am using loopback on server side of my application , to fetch and validate a data from database I'm using findOne method which is having a callback function. I wanted to get run the callback function as soon as the findone function is executed, The code i have written is working but i want to avoid usage of async-await. Any other alternative for this?
What I tried
function validId(req) {
const filter = {
where: {
ID: req.id,
}
};
//
const result = await model.findOne(filter);
if (result) {
return true;
} else {
return false;
}
}
module.exports = function () {
return async function validateTenant(req, res, next) {
var id = false;
if (req.url.includes("XYZ")) {
id = await validId(req)
}
//
if (id || !req.url.includes("XYZ")") {
next();
} else {
res.writeHead(404, { "Content-Type": "text/html" });
var html = fs.readFileSync(
"error.html"
);
res.end(html);
}
};
};
you could use the .then() function of the promise
model.findOne(filter).then((result)=>{
//execute the rest of the function that need to be executed after the findOne.
});
// The code will continue to execute while model.findOne is doing it's thing.
But if you want to wait for the FindOne to give a result without using await or the .then it is not possible unless you make a wrapper of findOne or your BDD package have a synchrone findOne
The following is pseudocode to illustrate my problem. The parent function must ultimately return a promise when all of the tasks are done (I've omitted the others for clarity). The parent function calls child functions and some of the child functions have to perform their tasks recursively and so, for clarity, I've separated them into worker functions. If there is a cleaner way I would love to learn it.
How best to handle the recursion in this example?
// This function must ultimately return a Promise.
async function parentFunction(uId) {
try {
await childFunction(uId);
return Promise.resolve(uId);
} catch (error) {
console.log(error);
}
}
async function childFunction(uId) {
try {
const done = await workerFunction(uId);
if (done) {
return Promise.resolve(true);
} else {
// There are more files to delete; best way to handle recursion?
}
} catch (error) {
console.log(error);
}
}
async function workerFunction(uId) {
try {
// Query the database, limit to 100 files.
const query = await db.queryFiles().limit(100);
if (query.size == 0) {
// Nothing to delete, we're done!
return Promise.resolve(true);
}
// Perform an atomic (all-or-none) batch delete that can only take 100 files at most.
await db.batchDelete(query);
// Batch delete successfull!
if (query.size < 100) {
// The query was less than 100 files so there can be no more files to delete.
return Promise.resolve(true);
} else {
// There may possibly be more files to delete.
// Return a promise or handle recursion here?
return Promise.resolve(false);
}
} catch (error) {
console.log(error);
}
}
just do recursion it's fine!
async function deleteFiles() {
const query = await db.queryFiles().limit(100)
if (query.size > 0) {
await db.batchDelete(query)
}
if (query.size === 100) {
return deleteFiles()
}
return true;
}
Similar to my last question but different enough that I am at a loss. I have the following function in AngularJs that I need to recreate in VueJs. I have two similar ways I've tried to write this in VueJs, but they are causing lots of site exceptions both ways.
AngularJs
var foo = function(obj, config) {
if (config.skip) {
return $q.reject("Skipping");
}
var deferred = $q.defer();
obj.promise = deferred.promise;
if (obj.hasValue()) {
deferred.resolve(obj);
} else {
"/api/callToApi".$promise.then(function(res) {
if (res) {
deferred.resolve(res);
else {
deferred.reject(res);
}
});
}
return deferred.promise;
}
VueJs - take 1. I'm pretty sure this one is missing the actual promise chaining, not sure how to correctly set that.
var foo = function(obj, config) {
let returnEarly = false;
let promise = new Promise((resolve, reject) => {
returnEarly = true;
reject("Skipping"):
}
if (returnEarly) {
return promise;
}
obj.promise = promise;
return new Promise((resolve, reject) => {
if (obj.hasValue()) {
resolve(obj);
} else {
axios.get("/api/callToApi").then(function(res) {
if (res) {
resolve(res);
} else {
reject(res);
}
}
}
}
}
Console errors with take 1
Uncaught (in promise) Error: Request failed with status code 404
at XMLHttpRequest.__capture__.onreadystatechange
VueJs - take 2. I thought this way would return the correct chaining, but I get an error Timeout - Async callback was not invoked within the 5000ms timeout when running jest tests.
var foo = function(obj, config) {
let returnEarly = false;
let promise = new Promise((resolve, reject) => {
returnEarly = true;
reject("Skipping"):
}
if (returnEarly) {
return promise;
}
obj.promise = promise;
return promise.then(() => {
return new Promise((resolve, reject) => {
if (obj.hasValue()) {
resolve(obj);
} else {
axios.get("/api/callToApi").then(function(res) {
if (res) {
resolve(res);
} else {
reject(res);
}
}
}
}
}
}
This is plain JavaScript, not specific to Vue.
It's generally unnecessary to assign a promise to obj.promise because it's returned from the function.
Excessive use of new Promise is known as promise constructor antipattern. If there's already a promise (Axios returns one), there's no need to create a new one, this results in redundant and error-prone code (this can be the reason for Async callback was not invoked... error). In case a new promise needs to be created, there are shortcut Promise methods.
Should be something like:
function(obj, config) {
if (config.skip) {
return Promise.reject("Skipping");
}
if (obj.hasValue()) {
return Promise.resolve(obj);
} else {
return axios("/api/callToApi").then(function(res) {
if (res)
return res;
else
throw res;
}
});
}
}
It's a bad practice in general to make errors anything but Error object.
Also notice that Axios response object is always truthy, possibly needs to be res.data.
Will be more concise when written as async..await.
I want to test the following storeCache function.
The critical part I want to test is the callback function in Meteor.call(...).
I want to mock Meteor.call(...) but it is wrapped by a Promise and the callback itself also relies on the wrapping Promise.
export async function storeCache(cache) {
// do something
return new Promise((resolve, reject) => {
Meteor.call("transferCache", cache, async (error, result) => {
if (error) {
reject(error);
} else {
try {
const result = await persistCache();
resolve(result)
} catch (e) {
reject(e);
}
}
});
});
}
What is the best way to test the defined callback function via Jest?
Or is there a better way to structure the Code to make it easiert to test?
it should be as simple as
// in your imports
import { Meteor } from 'meteor/meteor'
jest.mock('meteor/meteor', () => {
return {
Meteor: {
call: jest.fn()
}
}
})
//in your test case
Meteor.call.mockImplementation((eventName, cache, callback) => {
// your assertions
// you can call the callback here
// you probably want to mock persistCache too
})
So here's my solution:
The Meteor.call with its promise is in a separate function called helper.transferData().
This removed the necessity for a callback. I can put the logic directly in
storeCache() and mock helper.transferData().
export async function transferData(cache) {
return new Promise((resolve, reject) => {
Meteor.call("transferCache", cache, async (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
export async function storeCache(cache) {
// do something
try{
// call meteor method
const transferResult = await helper.transferData(cache);
// callback logic
const result = await persistCache();
// return Promise for compability reasons
return Promise.Resolve(result);
} catch (e) {
return Promise.Reject(result);
}
I started learning Promises in JS and I am trying to replace my existing callback logic using promises. I wrote a function which returns a new promise, and also uses a promise of a database instance to retrieve the data. However, i am not sure if i am doing it right. Here is the code snippet,
usersService.js
var getUsers = function(queryObject) {
return new Promise(function(resolve, reject) {
dbConnection.find(queryObject)
.then(function(result) {
if (result.length > 0) {
resolve(result)
} else {
resolve(errorMessage.invalidUser())
}).catch(function(err) {
reject(err)
});
})
};
usersRouter.js
router.get('/users', function (req,res,next) {
var queryObject = { "userId":req.query.userId };
userService.getUsers(queryObject)
.then(function (data) { //some logic })
.catch(function (err) { //some logic });
});
Can i use resolve conditionally ?
If the answer is No, what is the right approach ?
Also, am i using the promise in a right manner, in the router?
Thanks in advance!
Since dbConnection.find returns a promise, you can return it directly and choose what will be pass when you will resolve it. No need to wrap it inside an other promise.
var getUsers = function (queryObject) {
return dbConnection.find(queryObject).then(function (result) {
if (result.length > 0) {
return result
} else {
return errorMessage.invalidUser()
}
})
};