I have a method in my Db wrapper form nodejs mysql as follow:
let mysql=require('mysql')
query(sql,args,count=0){
let con=mysql.createConnection(configs[this.config])
return new Promise((resolve,reject)=>{
con.query(sql,args,(e,r)=>{
if(e){
if(e.code=='ER_CON_COUNT_ERROR'){
reject(e)
}
r=false
}
con.end(e=>{
return resolve(r)
})
})
}).catch(e=>{
//want to delay this call
if(count<5){
return this.query(sql, args,count+1)
}
return false
})
}
In case of failure I am recursively calling query but it happens instantaneously. I want to delay this call depending upon count which decide the number of retry.
This is how I am using it:
let test=async()=>{
let promises=[]
for(let i=1;i<100;i++){
promises.push(db.query('SELECT * FROM verbs'))
}
let r=await Promise.all(promises)
console.log(r[r.length-1],r.length)
}
test();
What is some good way to do that? I tried using setTimeout but that messed up promise chain and I wasn't able to exit in case of success.
Try this inside catch,
let mysql=require('mysql')
query(sql,args,count=0){
let con=mysql.createConnection(configs[this.config])
return new Promise((resolve,reject)=>{
con.query(sql,args,(e,r)=>{
if(e){
if(e.code=='ER_CON_COUNT_ERROR'){
reject(e)
}
r=false
}
con.end(e=>{
return resolve(r)
})
})
}).catch(e=>{
//want to delay this call
return new Promise((resolve,reject)=>{
setTimeout(()=>{
this.query(sql, args,count+1).then(resolve).catch(reject);
},2000);
});
})
}
Not sure why do you need infinite recursive call during exception. You have to change your design to limit retry.
Anyhow for the above given scenario, Below code can help
let mysql=require('mysql')
query(sql,args,count=0){
return new Promise((resolve,reject)=>{
let query_data = () => {
let con=mysql.createConnection(configs[this.config])
con.query(sql,args,(e,r)=>{
if(e){
if(e.code=='ER_CON_COUNT_ERROR'){
reject(e)
}
r=false
}
con.end(e=>{
return resolve(r)
})
})
}
try {
query_data();
} catch(e) {
// statements
console.log(e);
setTimeout(query_data, 3000);
}
})
}
Related
The most common implementation of a sleep function in javascript is returning a Promise after setTimeout resolves:
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
I have for loop with await sleep to keep it from executing too fast, such as not requesting xhr too fast. I also have a isBreak flag elsewhere to tell me when to stop the for loop. However, the issue I have is that when I break the for loop, the previous await sleep has already executed and is holding up the for loop. Is there a better way of breaking the for loop and also terminating the await sleep instantaneously?
const items = [];
let isBreak = false; // Somewhere else in the application
for (const item of items) {
if (isBreak) break;
// Do something, like xhr request
await sleep(15000); // 15 seconds sleep
if (isBreak) break;
}
Is there a way for me to signal for early
In JS, when an await operation starts, it can no longer be interrupted; it will wait until its operand promise is settled.
So, you have to make the promise you're awaiting cancelable in some way.
Unfortunately, your code can't get notified about a variable reassignment (when you set isBreak to true), and polling it would be inefficient.
Instead of a flag, you could use an AbortSignal (which was invented for this purpose), and make your sleep accept one:
function sleep(ms, signal) {
return new Promise((resolve, reject) => {
signal.throwIfAborted();
const timeout = setTimeout(() => {
resolve();
signal.removeEventListener('abort', abort);
}, ms);
const abort = () => {
clearTimeout(timeout);
reject(signal.reason);
}
signal.addEventListener('abort', abort);
});
}
Then, you use it like this:
const items = [];
const isBreak = new AbortController(); // Somewhere else in the application, call `isBreak.abort()`
try {
for (const item of items) {
// Do something, like xhr request
await sleep(15000, isBreak.signal); // 15 seconds sleep
}
} catch (e) {
if (e.name === 'TimeoutError') {
// Handle a cancellation
console.log('Cancelled');
} else {
// Not a cancellation, rethrow it
throw e;
}
}
An AbortSignal works well with fetch as well, in case you have to cancel that too.
An answer i found in a blog in the past that i adjusted. It is similiar to FZs answer. Same usage, too. Just to give an alternative.
relevant too: How to cancel timeout inside of Javascript Promise?
function sleep(ms, abortSignal) {
return new Promise((resolve, reject) => {
signal.addEventListener("abort", abort);
if(abortSignal.aborted){
abort();
}
const timeout = setTimeout(end, ms);
function abort() {
clearTimeout(timeout);
abortSignal.removeEventListener("abort", abort);
reject(new Error("sleep aborted"));
}
function end() {
abortSignal.removeEventListener("abort", abort);
resolve();
}
});
}
This is the code I have to so far. I need help with my test function using jest. Visual studio points error to function addTask("task 3) in the test function. The rest of the code executes fine i.e I am able to display a success message after adding task to the array. The test should pass this requirement of displaying success message only after adding the task to the array.
function successMessage(callback){
callback("Task3");
console.log('success')
}
var tasks=[];
tasks=[{task_content:"Task 1",status:"open"},
{task_content:"Task 2",status:"closed"}];
//Function addTask adds specific task to tasks array
function addTask(add_task) {
var status="open";
var new_task={task_content:add_task,status:status};
tasks.push(new_task);
console.log("After adding on Task 3");
console.log(tasks);
}
console.log("Initially Tasks array: ");
console.log(tasks);
successMessage(addTask)
test('success msg', done => {
function addTask("task3"){
try{
expect("task 3").toEqual({task_content: 'task3', status: 'open'})
done()
}catch(error){
done(error)
}
}
successMessage(addTask);
})
I had converted the above problem into using a promise. I was able to produce the test case with simpler understanding upon using and returning the value of promise rather than callbacks. I am still a noob but this is what has been working for me so far.
var tasks=[];
tasks=[{task_content:"Task 1",status:"open"},
{task_content:"Task 2",status:"closed"}];
function addTask(add_task) {
var status="open";
var new_task={task_content:add_task,status:status};
tasks.push(new_task);
console.log("After adding on Task 3");
console.log(tasks);
}
function successMessage(){
console.log('success')
}
function first(){
var promise1 = new Promise(function(resolve,reject){
resolve(addTask('task 3'))
})
return promise1;
}
function second(){
var promise2 = new Promise(function(resolve,reject) {
//addTask("task 3")
console.log('success')
resolve(second)
})
return promise2;
}
first();
second();
test('promise',()=>{
return first().then(()=>{
expect(second()).resolves.toBe('success')
})
//expect(first()).then(second()).resolves.toBe('success')
})
Try This
function addTask(add_task) {
var status="open";
var new_task={task_content:add_task,status:status};
tasks.push(new_task);
console.log("After adding on Task 3");
console.log(tasks);
}
function successMessage(){
console.log('success')
}
function first(){
var promise1 = new Promise(function(resolve,reject){
resolve(addTask('task 3'))
})
return promise1;
}
function second(){
var promise2 = new Promise(function(resolve,reject) {
//addTask("task 3")
console.log('success')
resolve(second)
})
return promise2;
}
first();
second();
test('promise',()=>{
return first().then(successCall=>{
expect(second()).resolves.toBe('success')
})
})
I'm trying to get a closure to return a value that is supposed to be updated once a promise is resolved (or rejected).
The following code works. Initially the internal variable from within the close returns NONE as expected.
Then the first Promise is launched, and once that is resolved, the internal variable is updated to FAIL.
The second Promise is a deliberate delay, just so that we can observe the change of the closured variable.
However, once the while loop is added to the equation, by uncommenting that loop(x) section, the update is not observable within the while loop.
I would expect to see this:
...
9963000000 NONE
9964000000 NONE
9965000000 NONE
9966000000 NONE
9967000000 NONE
9968000000 FAIL
9969000000 FAIL
9970000000 FAIL
9971000000 FAIL
9972000000 FAIL
9973000000 FAIL
9974000000 FAIL
...
I know it might be due to the single threaded blocking, but, is there a way to observe a dynamic external variable from within the while loop?
let sleep = async (ms) => new Promise ((resolve, reject) => setTimeout (resolve, ms));
let task = async (ms) => new Promise (function(resolve, reject) {
setTimeout (function(){
const error = true;
let result;
if(error){
result = '_NO_';
reject({'state': false, 'response': result});
}else{
result = '_YES_';
resolve({'state': true, 'response': result});
}
}, ms);
});
let loop = async (cb) => {
let i = 0;
while(i<10000000000){
const value = cb.getResponse();
(function() {
if(i%1000000==0){ console.log(i, value) };
i += 1;
})(i, value);
}
}
const linkResponse = (function(){
let response = 'NONE';
function setResponse(value) {response = value; return response};
function getResponse() {return response};
return { 'setResponse': setResponse, 'getResponse': getResponse };
});
const x = linkResponse();
console.log(x.getResponse());
(async () => {
task(3000)
.then(function(res){
console.log('__OK__', res);
let response = 'SUCCESS';
x.setResponse(response)
})
.catch(function(err){
console.log('error', err);
let response = 'FAIL';
x.setResponse(response)
});
sleep(6000)
.then(function(res){
console.log(x.getResponse())
});
//loop(x);
})();
Well, thanks for the help. Just as I was suspecting, it is indeed a blocked thread issue. I solved the problem with a recursive function. I just needed to have a long process running in the background and I naively thought that an infinite loop will do the job.
let loop2 = function(i, cb) {
if(i>100000){
return
}
console.log(i, cb.getResponse());
i += 1;
sleep(0)
.then(function(res){
loop2(i, cb);
});
}
And then calling:
loop2(0, x);
there is url: "http:/myurl/mydata/pages/"
the task is call url+"1",url+"2",...,until there is no correspondent page, number of pages is unknown
I am not sure about design of the decision. I know I can you "async await" approach, can I avoid it? Is the next approach ok?
let url= "http:/myurl/mydata/pages/";
let finished=true;
let page=1;
let datas={};
while(!finished) {
fetch(url+page)
.then(function(data) {
collectData(page,data);
page+=1;
})
.catch(function(error) {
finished=true;
});
}
function collectData(page,data){
datas[page]=data;
}
Can I use Promises for this task?
If you want a async-await approach:
async function fetchPages(url, page) {
try{
const data = await fetch(url+page);
collectData(page+url, data);
await fetchPages(url, page + 1);
} catch(e) {
return 'Its over';
}
}
};
This looks like a job for recursion!
let url= "http:/myurl/mydata/pages/";
let page=1;
let datas={};
recurse().then(/*...*/)
function recurse() {
return new Promise(function(resolve, reject) {
fetch(url+page)
.then(function(data) {
collectData(page,data);
page+=1;
return recurse()
})
.catch(function(error) {
resolve()
});
})
}
function collectData(page,data){
datas[page]=data;
}
NOTE: I didn't test this, so might not work exactly as written. But it should set you on the right track.
I'm tackling a project that requires me to use JavaScript with an API method call. I'm a Java programmer who has never done web development before so I'm having some trouble with it.
This API method is asynchronous and it's in a while loop. If it returns an empty array, the while loop finishes. Otherwise, it loops. Code:
var done = true;
do
{
async_api_call(
"method.name",
{
// Do stuff.
},
function(result)
{
if(result.error())
{
console.error(result.error());
}
else
{
// Sets the boolean to true if the returned array is empty, or false otherwise.
done = (result.data().length === 0) ? true : false;
}
}
);
} while (!done);
This doesn't work. The loop ends before the value of "done" is updated. I've done some reading up on the subject and it appears I need to use promises or callbacks because the API call is asynchronous, but I can't understand how to apply them to the code I have above.
Help would be appreciated!
edit: see the bottom, there is the real answer.
I encourage you yo use the Promise API. Your problem can be solved using a Promise.all call:
let promises = [];
while(something){
promises.push(new Promise((r, j) => {
YourAsyncCall(() => r());
});
}
//Then this returns a promise that will resolve when ALL are so.
Promise.all(promises).then(() => {
//All operations done
});
The syntax is in es6, here is the es5 equivalent (Promise API may be included externally):
var promises = [];
while(something){
promises.push(new Promise(function(r, j){
YourAsyncCall(function(){ r(); });
});
}
//Then this returns a promise that will resolve when ALL are so.
Promise.all(promises).then(function(){
//All operations done
});
You can also make your api call return the promise and push it directly to the promise array.
If you don't want to edit the api_call_method you can always wrap your code in a new promise and call the method resolve when it finishes.
edit: I have seen now the point of your code, sorry. I've just realized that Promise.all will not solve the problem.
You shall put what you posted (excluding the while loop and the control value) inside a function, and depending on the condition calling it again.
Then, all can be wraped inside a promise in order to make the external code aware of this asynchronous execution. I'll post some sample code later with my PC.
So the good answer
You can use a promise to control the flow of your application and use recursion instead of the while loop:
function asyncOp(resolve, reject) {
//If you're using NodeJS you can use Es6 syntax:
async_api_call("method.name", {}, (result) => {
if(result.error()) {
console.error(result.error());
reject(result.error()); //You can reject the promise, this is optional.
} else {
//If your operation succeeds, resolve the promise and don't call again.
if (result.data().length === 0) {
asyncOp(resolve); //Try again
} else {
resolve(result); //Resolve the promise, pass the result.
}
}
});
}
new Promise((r, j) => {
asyncOp(r, j);
}).then((result) => {
//This will call if your algorithm succeeds!
});
/*
* Please note that "(...) => {}" equivals to "function(...){}"
*/
sigmasoldier's solution is correct, just wanted to share the ES6 version with async / await:
const asyncFunction = (t) => new Promise(resolve => setTimeout(resolve, t));
const getData = async (resolve, reject, count) => {
console.log('waiting');
await asyncFunction(3000);
console.log('finshed waiting');
count++;
if (count < 2) {
getData(resolve, reject, count);
} else {
return resolve();
}
}
const runScript = async () => {
await new Promise((r, j) => getData(r, j, 0));
console.log('finished');
};
runScript();
If you don't want to use recursion you can change your while loop into a for of loop and use a generator function for maintaining done state. Here's a simple example where the for of loop will wait for the async function until we've had 5 iterations and then done is flipped to true. You should be able to update this concept to set your done variable to true when your webservice calls have buffered all of your data rows.
let done = false;
let count = 0;
const whileGenerator = function* () {
while (!done) {
yield count;
}
};
const asyncFunction = async function(){
await new Promise(resolve => { setTimeout(resolve); });
};
const main = new Promise(async (resolve)=>{
for (let i of whileGenerator()){
console.log(i);
await asyncFunction();
count++;
if (count === 5){
done = true;
}
}
resolve();
});
main.then(()=>{
console.log('all done!');
});
Also you may try recursion solution.
function asyncCall(cb) {
// Some async operation
}
function responseHandler(result) {
if (result.error()) {
console.error(result.error());
} else if(result.data() && result.data().length) {
asyncCall(responseHandler);
}
}
asyncCall(responseHandler);
Here is a solution I came up with. Place this in an async function.
let finished = false;
const loop = async () => {
return new Promise(async (resolve, reject) => {
const inner = async () => {
if (!finished) {
//insert loop code here
if (xxx is done) { //insert this in your loop code after task is complete
finshed = true;
resolve();
} else {
return inner();
}
}
}
await inner();
})
}
await loop();
If you don't want to use Promises you can restructure your code like so:
var tasks = [];
var index = 0;
function processNextTask()
{
if(++index == tasks.length)
{
// no more tasks
return;
}
async_api_call(
"method.name",
{
// Do stuff.
},
function(result)
{
if(result.error())
{
console.error(result.error());
}
else
{
// process data
setTimeout(processNextTask);
}
}
);
}
Your loop won't work, because it is sync, your async task is async, so the loop will finish before the async task can even respond. I'd reccomend you to use Promises to manage async tasks:
//first wrapping your API into a promise
var async_api_call_promise = function(methodName, someObject){
return new Promise((resolve, reject) => {
async_api_call(methodName, someObject, function(result){
if(result.error()){
reject( result.error() )
}else{
resolve( result.data() )
}
});
})
}
now to your polling code:
//a local utility because I don't want to repeat myself
var poll = () => async_api_call_promise("method.name", {/*Do stuff.*/});
//your pulling operation
poll().then(
data => data.length === 0 || poll(), //true || tryAgain
err => {
console.error(err);
return poll();
}
).then((done) => {
//done === true
//here you put the code that has to wait for your "loop" to finish
});
Why Promises? Because they do state-management of async operations. Why implement that yourself?
let taskPool = new Promise(function(resolve, reject) {
resolve("Success!");
});
let that = this;
while (index < this.totalPieces) {
end = start + thisPartSize;
if (end > filesize) {
end = filesize;
thisPartSize = filesize - start;
}
taskPool.then(() => {
that.worker(start, end, index, thisPartSize);
});
index++;
start = end;
}