My app makes some work then makes query to db. It is reaping every 3 seconds, the app must have only 1 active connection to db, if connection is active must wait then do it, I have tried this:
var state = false;
let l = 0;
function sleep(ms) {
console.log('object');
l++;
return new Promise(resolve => setTimeout(resolve, ms));
}
async function lol(orderid) {
if (state == true) {
await sleep(1000 + l * 1000);
let k = await lol(orderid);
return k;
};
state = true;
let res = await getOrders(`(${orderid})`, 1500, 20)
state = false;
return res;
}
let orderid = 14136377;
setInterval(async () => {
let m = await lol(orderid++);
console.log(m[0]['json_build_object']['order']['orderid']);
}, 1000);
Now it's making query for sorted ID witch every time incremented and output it.This is results
condition if (state == true || l >0 ) don't give response,all interval calls stays alive.
As you can see sometimes it's not consecutive. How to make it consecutive?
Are there any better ways to make this ?
If think this will be enough.
let state = false;
let data = [];
getorerids().then(res => {
if(state){
data.push(res);
}else {
state = true;
dbcall(res);
}
});
dbcall(ids)
.then(res =>{
dosmtwithids(res);
if(data[0]){
return dbcall(data.pop());
}else {
state = false;
}
});
dosmtwithids(){
.......
};
setInterval(getorerids, 3000);
Related
I want my function to execute X(=3) times until success.
In my situation I'm running kinesis.putRecord (from AWS API), and if it fails - I want to run it again until it succeeds, but not more than 3 tries.
I'm new to NodeJS, and the code I wrote smells bad.
const putRecordsPromise = function(params){
return new Promise((resolve, reject) => {
kinesis.putRecord(params, function (err, data) {
resolve(err)
});
})
}
async function waterfall(params){
try{
let triesCounter = 0;
while(triesCounter < 2){
console.log(`try #${triesCounter}`)
let recordsAnswer = await putRecordsPromise(params)
if(!recordsAnswer){
console.log("success")
break;
}
triesCounter += 1;
}
// continue ...
} catch(err){
console.error(err)
}
}
waterfall(params)
I promise the err result. Afterwards, If the err is empty, then all good. otherwise, continue running the same command.
I'm sure there is a smarter way to do this. Any help would be appreciated.
I think, all the Aws functions can return a Promise out of the box, then you can just put the call into try/catch:
let triesCounter = 0;
while(triesCounter < 2){
console.log(`try #${triesCounter}`)
try {
await kinesis.putRecord(params).promise();
break; // 'return' would work here as well
} catch (err) {
console.log(err);
}
triesCounter ++;
}
In functional style:
...
await tryUntilSucceed(() => kinesis.putRecord(params).promise());
...
async function tryUntilSucceed(promiseFn, maxTries=3) {
try {
return await promiseFn();
} catch (e) {
if (maxTries > 0) {
return tryUntilSucceed(promiseFn, maxTries - 1);
}
throw e;
}
}
Make a little module, say try-and-try-again.js:
exports = module.exports = tryAndTryAgain;
function tryAndTryAgain( maxTries, thisContext , fn, ...argv) {
let success = false;
for (let i = i ; i < maxTries && !success ; ++i ) {
let rc = fn.apply(thisContext, args);
success = rc == 0 ? true : false;
}
return success;
}
Then you can use it anywhere:
const tryAndTryAgain = require('./try-and-try-again');
function somethingThatMightNeedARetry() { ... }
const succeeded = tryAndTryAgain( 3 , null, somethingThatMightNeedARetry, 'arg-1', 'arg-2', 'arg-3' );
There is an npm package called async-retry that is pretty handy. It acts as a wrapper for your function and retries if anything throws (with some exceptions that you can handle, see their example below).
// Packages
const retry = require('async-retry')
const fetch = require('node-fetch')
await retry(async bail => {
// if anything throws, we retry
const res = await fetch('https://google.com')
if (403 === res.status) {
// don't retry upon 403
bail(new Error('Unauthorized'))
return
}
const data = await res.text()
return data.substr(0, 500)
}, {
retries: 5
})
I have a for loop function in NodeJS. I would like to wait until the result of Http Get request is completed in For Loop before it executes the next iteration, how do I achieve that?
for (let k=0; k<fd.length; k++) {
url = fd[k].nct_id;
HttpSearch({condition: url}).then(trials => {
//Get the result first before execute the next iteration
console.log(trials);
});
}
You should make the for-loop async:
const main = async () => {
for (let k = 0; k < fd.length; k++) {
const url = fd[k].nct_id;
const trials = await HttpSearch({ condition: url });
console.log(trials);
}
};
main().catch(console.error);
This will cause the loop to "pause" at each HttpSearch.
I will do like this
let k = 0 ;
let len = fd.length;
for (; k > len;) {
let url = fd[k].nct_id;
let subs = await HttpSearch({condition: url});
console.log(subs);
k++
}
or like this with promise
let url;
let promiseChain = Promise.resolve();
for (let i = 0; i < fd.length; i++) {
url = fd[k].nct_id;
// you need to pass the current value of `url`
// into the chain manually, to avoid having its value
// changed before the .then code accesses it.
const makeNextPromise = (url) => () => {
HttpSearch({condition: url})
.then((result) => {
// return promise here
return result
});
}
promiseChain = promiseChain.then(makeNextPromise(url))
}
This is using recursion, which calls next, once previous is finished
var limit = fd.length;
var counter = 0;
HttpSearch({condition: fd[0].nct_id;}).then(yourCallBack);
function yourCallBack(trials){
console.log(trails);
if(counter == limit)
return console.log('Done')
HttpSearch({condition: fd[counter].nct_id;}).then(yourCallBack);
counter++;
}
Here is the structure of my firebase database:
/UserData
/DeviceMgmt
/Counters
/NumberOfAll:
/NumberOfSelected
/TotalDownloaded
...
/Devices
/pushId1
/uid
/signOutTime
/toSelect=true (optional)
/downloaded
/lastDownload
/pushId2
/pushId3
...
And this is my cloud function:
exports.markDevicesForDownload = functions.database.ref('/UserData/DeviceMgmt/Counters/NumberOfSelected').onUpdate( (change) => {
const changeRef = change.after.ref;
const deviceMgmtRef = changeRef.parent.parent; // /UserData/DeviceMgmt
if (change.after.val() === 0 ) { //NumberOfSelected gets 0 value
return deviceMgmtRef.once('value')
.then((snap) => {
const devicesRef = snap.child('Devices').ref;
var average;
var numberOfAllDevices;
var totalDownloaded;
numberOfAllDevices = snap.child('Counters/NumberOfAll').val();
totalDownloaded = snap.child('Counters/TotalDownloaded').val();
average = Math.round(totalDownloaded/numberOfAllDevices);
return devicesRef
.orderByChild('signOutTime')
.equalTo(0)
.once('value',(devices) => {
return devices.ref
.orderByChild('downloaded')
.endAt(average)
.once('value',(devices) => {
devices.forEach((device) => {
device.child('toSelect').ref.set(true);
});
});
});
});
} else {
return false;
}
});
The function triggers when the counter NumberOfSelected = 0;
This happens when under any of device pushId there is no child toSelect. Then the query on downloaded child makes all devices with downloaded less than average set toSelect=true.
I wanted to limit the devices only to those which have signOutTime equal 0.
Somehow that query does not work and all devices are considered.
What I did wrong???
I would push all async tasks into a promise array and then return them all when all tasks complete:
exports.markDevicesForDownload = functions.database.ref('/UserData/DeviceMgmt/Counters/NumberOfSelected').onUpdate((change) => {
const changeRef = change.after.ref;
const deviceMgmtRef = changeRef.parent.parent; // /UserData/DeviceMgmt
if (change.after.val() === 0) { //NumberOfSelected gets 0 value
return deviceMgmtRef.once('value')
.then((snap) => {
const promises = [];
const devicesRef = snap.child('Devices').ref;
var average;
var numberOfAllDevices;
var totalDownloaded;
numberOfAllDevices = snap.child('Counters/NumberOfAll').val();
totalDownloaded = snap.child('Counters/TotalDownloaded').val();
average = Math.round(totalDownloaded / numberOfAllDevices);
const dR = devicesRef
.orderByChild('signOutTime')
.equalTo(0)
.once('value', (devices) => {
const dW = devices.ref
.orderByChild('downloaded')
.endAt(average)
.once('value', (devices) => {
devices.forEach((device) => {
if (device.child("signOutTime").val() === 0){
promises.push(device.child('toSelect').ref.set(true));
}
});
});
promises.push(dW);
});
promises.push(dR);
return Promise.all(promises);
});
} else {
return false;
}
});
Lets say I have a file file1.js which contains:
const a = true;
let b;
if (a) {
b = c;
}
if (!a) {
b = d;
}
Now when I run test case on this file my first condition gets covered. Is there any way that I can cover second condition by setting a to false or should I change my code in a way that I can call a method with different values to test for each case kind of like:
const a = true;
getBVal(a) {
return a ? c : d;
}
let b = getBVal(a);
Update:
Below is my code for requestAnimationFrame with fallbacks for older browsers:
let lastTime = 0;
const vendors = ["ms", "moz", "webkit", "o"];
let rAF = window.requestAnimationFrame;
if (!rAF) {
rAF = vendors.find(prefix => window[`${prefix}RequestAnimationFrame`]);
}
if (!rAF) {
rAF = cB => {
const currTime = new Date().getTime();
const timeToCall = Math.max(0, 16 - (currTime - lastTime));
const id = setTimeout(() => {
cB(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
function requestAnimationFrame(callback) {
return rAF(callback);
}
export default requestAnimationFrame;
I am using jsdom in setup for window object. Now if I have to test case for window.requestAnimationFrame = null its not possible in the way I have written my code
Now after changing it to:
import { requestAnimationFrameVendor } from "../constants";
let lastTime = 0;
const customFn = cB => {
const currTime = new Date().getTime();
const timeToCall = Math.max(0, 16 - (currTime - lastTime));
const id = setTimeout(() => {
cB(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
function requestAnimationFrame(callback) {
const rAF = window.requestAnimationFrame;
return rAF && rAF(callback) || requestAnimationFrameVendor && requestAnimationFrameVendor(callback) || customFn(callback);
}
export default requestAnimationFrame;
And then if I write test like:
import * as constants from "../../constants";
describe("animationFrame", () => {
let requestAnimationFrame;
let cancelAnimationFrame;
beforeAll(() => {
requestAnimationFrame = global.window.requestAnimationFrame;
cancelAnimationFrame = global.window.cancelAnimationFrame;
});
test("requestAnimationFrame", done => {
global.window.requestAnimationFrame = null;
global.window.cancelAnimationFrame = null;
const requestId1 = Utils.requestAnimationFrame(jest.fn());
constants.requestAnimationFrameVendor = jest.fn(() => {
return requestAnimationFrame;
});
const requestId2 = Utils.requestAnimationFrame(jest.fn());
setTimeout(() => {
Utils.cancelAnimationFrame(requestId1);
Utils.cancelAnimationFrame(requestId2);
done();
}, 300);
});
afterEach(() => {
global.window.webkitRequestAnimationFrame = null;
global.window.webkitCancelAnimationFrame = null;
});
});
Then it covers all the conditions.
I would go the second rout (getBVal()) because it makes your interface more testable.
In general, removing global state (like your const a or let b) will make your code and interfaces more testable. If you cannot remove the global state completely, you can introduce abstractions so that your test does not need to know about the global state (like your suggested getBVal()).
Maybe you can go even further and remove the global b: Instead always call getBVal() instead? The performance impact will be negligible in most cases, and your code becomes even more testable and less coupled...
It is 2016, Node has had nearly full ES6 support since v4, and Promises have been around since 0.12. It's time to leave callbacks in the dust IMO.
I'm working on a commander.js-based CLI util which leverages a lot of async operations - http requests and user input. I want to wrap the Commander actions in async functions so that they can be treated as promises, and also to support generators (useful for the co-prompt library I'm using for user input).
I've tried wrapping the CB with co in two ways:
1)
program.command('myCmd')
.action(program => co(function* (program) {...})
.catch(err => console.log(err.stack)) );
and
2) program.command('myCmd').action(co.wrap(function* (program) { .. }));
The problem with 1) is that the program parameter isn't passed
The problem with 2) is that errors are swallowed...
I'd really like to get this working as it yields much nicer code in my use case - involving a lot of http requests and also waiting for user input using the co-prompt library..
Is it a better option altogether perhaps to wrap program.Command.prototype.action somehow?
thanks!
I've used a bespoke version of something like co to get a db.exec function which uses yield to do database request. You can pass parameters into a generator function (I pass in a connection object - see the comment where I do it).
Here is by db.exec function that is very similar to what co does
exec(generator) {
var self = this;
var it;
debug('In db.exec iterator');
return new Promise((accept,reject) => {
debug('In db.exec Promise');
var myConnection;
var onResult = lastPromiseResult => {
debug('In db.exec onResult');
var obj = it.next(lastPromiseResult);
if (!obj.done) {
debug('db.exec Iterator NOT done yet');
obj.value.then(onResult,reject);
} else {
if (myConnection) {
myConnection.release();
debug('db.exec released connection');
}
accept(obj.value);
debug('db.exec Promise Resolved with value %d',obj.value);
}
};
self._connection().then(connection => {
debug('db.exec got a connection');
myConnection = connection;
it = generator(connection); //This passes it into the generator
onResult(); //starts the generator
}).catch(error => {
logger('database', 'Exec Function Error: ' + error.message);
reject(error);
});
});
}
the connection object also wraps by database connection object and provides a generator function ability to process the rows of the results from the database, but I won't post that here (although the example below is using it to process the rows).
Here is an example of using the exec function to run a sequence of sql
db.exec(function*(connection) {
if (params.name === ADMIN_USER) {
debug('Admin Logon');
user.name = ADMIN_DISPLAY;
user.keys = 'A';
user.uid = 0;
let sql = 'SELECT passwordsalt FROM Admin WHERE AdminID = 0';
connection.request(sql);
yield connection.execSql(function*() {
let row = yield;
if (row) {
user.nopass = (row[0].value === null);
} else {
user.nopass = false;
}
debug('Admin Password bypass ' + user.nopass.toString());
});
} else {
debug('Normal User Logon');
let sql = `SELECT u.UserID,PasswordSalt,DisplayName,AccessKey,l.LogID FROM Users u
LEFT JOIN UserLog l ON u.userID = l.userID AND DATEDIFF(D,l.LogDate,GETDATE()) = 0
WHERE u.UserName = #username`;
let request = connection.request(sql);
request.addParameter('username',db.TYPES.NVarChar,params.name);
let count = yield connection.execSql(function*() {
let row = yield;
if (row) {
user.uid = row[0].value;
user.name = row[2].value;
user.keys = (row[3].value === null) ? '' : row[3].value;
user.nopass = (row[1].value === null) ;
user.lid = (row[4].value === null) ? 0 : row[4].value;
debug('Found User with uid = %d and lid = %d, keys = %s',
user.uid, user.lid, user.keys);
}
});
if (count === 0) {
debug('Not Found User');
// couldn't find name in database
reply(false,false);
return;
}
}
if (!user.nopass) {
debug('Need a Password');
//user has a password so we must check it
passGood = false; //assume false as we go into this
let request = connection.request('CheckPassword');
request.addParameter('UserID',db.TYPES.Int,user.uid);
request.addParameter('password',db.TYPES.VarChar,params.password);
yield connection.callProcedure(function*() {
let row = yield;
if (row) {
//got a valid row means we have a valid password
passGood = true;
}
});
} else {
passGood = true;
}
if (!passGood) {
debug('Not a Good Pasword');
reply(false,true);
} else {
if (user.uid !== 0 && user.lid === 0) {
let sql = `INSERT INTO UserLog(UserID,LogDate,TimeOn,UserName) OUTPUT INSERTED.logID
VALUES(#uid,GETDATE(),GETDATE(),#username)`;
let request = connection.request(sql);
request.addParameter('uid',db.TYPES.Int,user.uid);
request.addParameter('username',db.TYPES.NVarChar,user.name);
yield connection.execSql(function*() {
let row = yield;
if (row) {
user.lid = row[0].value;
debug('Users Log Entry = %d',user.lid);
}
});
}
reply(true,user);
}
})
.catch((err) => {
logger('database','Error on logon: ' + err.message);
reply(false,false);
});
});
There is a quite simple way to do async function in Commander.js
async function run() {
/* code goes here */
}
program
.command('gettime')
.action(run);
program.parse(process.argv);