So my thinking was right, when recursing with promises, we end up calling all chained callbacks for however many times we recurse, for example
function p() {
return new Promise(function (r) {
process.nextTick(r);
})
}
function recurse(count) {
return p().then(function () {
if (count < 10) {
console.log('count => ', count);
return recurse(++count);
}
}).then(function(){
console.log('a');
return 5;
});
}
recurse(1).then(function () {
console.log('done');
});
If you run the above, we get:
count => 1
count => 2
count => 3
count => 4
count => 5
count => 6
count => 7
count => 8
count => 9
a
a
a
a
a
a
a
a
a
a
done
Is there a way to register this callback with console.log('a') just once instead of registering it 10 times?
I don't think I want/need this function to be called 10 times, and would like to find a way to have it called just once.
I am actually just as interested in a similar solution for Observables, specifically RxJS5 Observables.
I guess the only solution is to nest the remainder of your code inside the first promise callback like so:
function p() {
return new Promise(function (r) {
process.nextTick(r);
})
}
function recurse(count) {
return p().then(function () {
if (count < 10) {
return recurse(++count);
} else {
// all your remaining code would have to go here
console.log('a');
return 5; // or return someOtherPromise() or anything
}
});
}
recurse(1).then(function () {
console.log('done');
});
If the recursion is synchronous, you can simply recurse within the .then's function
new Promise(res => {
res(); // dummy initial promise
}).then(() => {
function recurse(x) { // recursion inside then
console.log('x', x);
if (x < 10) return recurse(++x);
return x;
}
return recurse(1); // begin recursion
}).then(y => { // this only fires once recursion above is resolved
console.log('y', y);
return 5;
}).then(z => console.log('z', z));
// x 1
// ... (synchronous)
// x 10
// y 10 (value passed from resolving inner promise)
// z 5 (value returned from previous then)
Or if our recursive function is asynchronous, we can have it return a promise too, so you end up with a recursion which looks like this
function doWork() {
function addOne(x) {
return new Promise((res, rej) => {
// async bit here
res(x + 1);
});
}
function recurse(x) {
if (x < 10) return addOne(x).then(recurse);
return x;
}
return recurse(1);
}
And in a promise chain, would look like this
new Promise(res => {
res(); // dummy initial promise
}).then(() => {
return doWork();
}).then(y => {
console.log(y);
});
Related
I wrote a function to get a random number after 500 milliseconds and it worked perfectly with (return new Promise (resolve,reject)), also, it worked without it by using setTimeout() function, but when I added the promise and setTimeout() inside it wont show the result, I have console.log() before and after the result and both are showing. Please note that i am still new to the promise concept in general since this is a task from my coding school, thank you.
My code:
function getRandomNumber () {
new Promise ((resolve,reject) =>{
console.log('Getting Random Number...');
let number = Math.random();
setTimeout( () => {
if(number){
resolve (`OK ${number}`);
console.log('Done!');
}
else
reject ('ERROR');
}, 500);
});
}
getRandomNumber();
Result:
First, return your promise, second use the result the promise provides either by creating a variable from it and awaiting it or using then
function getRandomNumber() {
return new Promise((resolve, reject) => {
console.log('Getting Random Number...');
let number = Math.random();
setTimeout(() => {
if (number) {
resolve(`OK ${number}`);
console.log('Done!');
} else {
reject('ERROR');
}
}, 500);
});
}
getRandomNumber().then(result => console.log(result));
(async () => {
const number = await getRandomNumber();
console.log(number);
})();
Another option is to write sleep as a separate reusable function. Note async function can await multiple values and return values of their own. Calling an async function will always return a Promise -
function sleep(time) {
return new Promise(resolve => setTimeout(resolve, time))
}
async function getRandomNumber() {
console.log('Getting Random Number...')
await sleep(500)
const number = Math.random()
console.log("Done!")
return number
}
async function main() {
const number1 = await getRandomNumber()
console.log("number1", number1)
const number2 = await getRandomNumber()
console.log("number1", number2)
const number3 = await getRandomNumber()
console.log("number1", number3)
return number1 + number2 + number3
}
main().then(console.log, console.error)
.as-console-wrapper { min-height: 100%; }
Getting Random Number...
Done!
number1 0.2907058138817884
Getting Random Number...
Done!
number1 0.05784624607512423
Getting Random Number...
Done!
number1 0.889702848981964
1.2382549089388766
You need to return the promise then you can use await inside async function to get the result
function getRandomNumber () {
return new Promise ((resolve,reject) =>{
console.log('Getting Random Number...');
let number = Math.random();
setTimeout( () => {
if(number){
resolve (`OK ${number}`);
console.log('Done!');
}
else
reject ('ERROR');
}, 500);
});
}
const printNumber = async () => console.log(await getRandomNumber());
printNumber();
I'm requesting to server "S" to get some data, but this data may not be ready.
When the data is not yet ready, server S responds with {"data":null,"state": "pending"} but when the data has been prepared the response will be something like {"data": {...somedata}, "state": "done"}.
I have to repeat the request until the data is ready. What I'm doing now is something like this:
let wait = function* () {
let t = 500;
for (let j = 1; j < 10; j++) {
yield new Promise((resolve) => {
setTimeout(() => resolve(), t*=2);
});
}
}();
let result = await sendRequestToS();
status = result;
for (let i = 0; i < 4 && result.state==='pending'; i++) {
await wait.next().value;
result = await sendRequestToS();
}
As you can see, I send the request up to 4 times with a delay of 1, 2, 4 and 8 seconds.
Am I doing this the right way?
Isn't that (using setTimeout to delay between requests) a bad practice?
I'd write this as such:
function wait(ms) {
return new Promise(res => setTimeout(res, ms));
}
async function requestAndRetry() {
let retries = 10;
let timeout = 1000;
while(retries>0) {
const response = await sendRequestToS();
if (result?.state === 'done') {
return result;
}
await wait(timeout);
retries--;
timeout*=2;
}
throw new Error('Request failed after 10 retries');
}
I don't think it's a bad idea. It's called exponential back-off and you're not blocking the script engine.
Instead of using generators directly, you could simply do this using async/await and recursion. Here's an example which tries to get the response a limited number of times in order to prevent an endless recursion and with a timeout between retries:
async function wait(timeInMs) {
console.log('Waiting ...');
return new Promise((resolve => setTimeout(() => resolve(), timeInMs)));
}
async function tryRequestToS(numberOfTries, timeout) {
if (numberOfTries <= 0) {
throw new Error("could net get result");
}
const result = await sendRequestToS();
if (result && result.state === "done") {
return result;
}
await wait(timeout); // wait for the defined timeout before recurring
return tryRequestToS(numberOfTries - 1, timeout);
}
(async () => {
try {
const result = await tryRequestToS(10, 500); // max. 10 retries, 500 ms delay between retries
console.log(result);
} catch(err) {
console.log(err);
}
})();
I've been introduced to the concepts of Microtasks and Macrotasks for a while now, and from everything I've read, I always thought setTimeout to be considered to create a macrotask and Promise.resolve() (or process.nextTick on NodeJS) to create microtasks.
(Yes, I'm aware that different Promise libraries like Q and Bluebird have different schedulers implementations, but here I'm referring to the native Promises on each platform)
With this in mind I'm unable to explain the following sequence of events on NodeJS (results on Chrome are different from NodeJS (both v8 LTS and v10) and match with my understanding on this subject).
for (let i = 0; i < 2; i++) {
setTimeout(() => {
console.log("Timeout ", i);
Promise.resolve().then(() => {
console.log("Promise 1 ", i);
}).then(() => {
console.log("Promise 2 ", i);
});
})
}
So, the results I have on Chrome (and that are consistent with my understanding of Micro/Macro tasks and how Promise.resolve and setTimeout behave) are:
Timeout 0
Promise 1 0
Promise 2 0
Timeout 1
Promise 1 1
Promise 2 1
The same code executed on NodeJS outputs:
Timeout 0
Timeout 1
Promise 1 0
Promise 2 0
Promise 1 1
Promise 2 1
I'm looking for a way to have the same results on NodeJS that I have on Chrome. I've also tested with process.nextTick instead of Promise.resolve() but the results are the same.
Can anyone point me into the right direction?
This was recognized by the NodeJs team as a bug, more details here: https://github.com/nodejs/node/issues/22257
Meantime it was already fixed and released has part of Node v11.
Best,
José
You can't control how different architectures queue the promises and timeouts.
Excellent Read Here: https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
If you want the same results you are going to have to chain promises.
let chain = Promise.resolve(null)
for (let i = 0; i < 2; i++) {
console.log("Chaining ", i);
chain = chain.then(() => Promise.resolve()
.then(() => {
setTimeout(() => {
console.log("Timeout ", i);
Promise.resolve()
.then(() => {
console.log("Promise 1 ", i);
})
.then(() => {
console.log("Promise 2 ", i);
})
}, 0)
}))
}
chain.then(() => console.log('done'))
I am not saying I got it right, I wrote something adhoc and I'd like you to test the below:
the wrapper:
function order(){
this.tasks = [];
this.done = false;
this.currentIndex = 0;
this.ignited = false;
}
order.prototype.push = function(f){
var that = this,
args = Array.prototype.slice.call(arguments).slice(1);
if(this._currentCaller){
this.tasks.splice(
this.tasks.indexOf(this._currentCaller) + 1 + (this.currentIndex++),
0,
function(){that._currentCaller = f; f.apply(this,args);}
);
} else {
this.tasks.push(function(){that._currentCaller = f; f.apply(this,args);});
}
!this.ignited && (this.ignited = true) && this.ignite();
return this;
}
order.prototype.ignite = function(){
var that = this;
setTimeout(function(){
if(that.tasks.length){
that.tasks[0]();
that.tasks.shift();
that.repeat(function(){that.reset(); that.ignite()});
} else {
that.ignited = false;
that.reset();
}
},0);
}
order.prototype.repeat = function(f){
var that = this;
if(this.done || !this.tasks.length){
f();
} else {
setTimeout(function(){that.repeat(f);},0);
}
}
order.prototype.reset = function(){
this.currentIndex = 0;
delete this._currentCaller;
this.done = false;
}
to use:
create an instance:
var x = new order;
then modify the rest a bit:
for (let i = 0; i < 2; i++) {
x.push(function(i){
setTimeout(() => {
console.log("Timeout ", i);
x.push(function(i){
Promise.resolve().then(() => {
console.log("Promise 1 ", i);
}).then(() => {
console.log("Promise 2 ", i);
x.done = true;
})
},i);
x.done = true;
});
},i);
}
I get this:
Timeout 0
Promise 1 0
Promise 2 0
Timeout 1
Promise 1 1
Promise 2 1
You can even elaborate a bit:
for (let i = 0; i < 2; i++) {
x.push(function(i){
setTimeout(() => {
console.log("Timeout ", i);
x.push(function(i){
Promise.resolve().then(() => {
console.log("Promise 1 ", i);
}).then(() => {
console.log("Promise 2 ", i);
x.done = true;
})
},i)
.push(function(i){
Promise.resolve().then(() => {
console.log("Promise 1 ", i);
}).then(() => {
console.log("Promise 2 ", i);
x.done = true;
})
},i+0.5)
.push(function(i){
Promise.resolve().then(() => {
console.log("Promise 1 ", i);
}).then(() => {
console.log("Promise 2 ", i);
x.done = true;
})
},i+0.75);
x.done = true;
});
},i);
}
In node v6, you get:
Timeout 0
Promise 1 0
Promise 2 0
Promise 1 0.5
Promise 2 0.5
Promise 1 0.75
Promise 2 0.75
Timeout 1
Promise 1 1
Promise 2 1
Promise 1 1.5
Promise 2 1.5
Promise 1 1.75
Promise 2 1.75
Would you try this in your node version for me? In my node (6.11, I know its old) it works.
Tested on chrome, firefox, node v6.11
Note: you don't have to keep reference to 'x', this within the pushed functions refer to the order instance. You can also use Object.defineProperties to render getters/setters unconfigurable, to prevent accidental deletion of instance.ignited etc.
What are the advantages using the chain with '.then' if it does not work like a expected:
new Promise(function(resolve, reject) {
// A mock async action using setTimeout
setTimeout(function() { resolve(10); }, 3000);
})
.then(function(num) {
console.log('first then: ', num); return num * 2; })
.then(function(num) {
setTimeout(function() {
console.log('second then: ', num); return num * 2; }, 500);
})
.then(function(num) {
console.log('last then: ', num);
});
// RESULT!
// From the console:
// first then: 10
// last then: undefined
// second then: 20
I was expecting for the following result:
// RESULT!
// From the console:
// first then: 10
// second then: 20
// last then: 40
The second then has to return another promise if you want the third then to fire after the timeout in the second then.
This version of your code will give you the desired result:
new Promise(function (resolve) {
setTimeout(function() {
resolve(10);
}, 3000);
})
.then(function (num) {
console.log('first then: ', num); return num * 2;
})
.then(function (num) {
return new Promise(function (resolve) {
setTimeout(function () {
console.log('second then: ', num);
resolve(num * 2);
}, 500);
});
})
.then(function (num) {
console.log('last then: ', num);
});
The reason why your code didn't work as expected was that the third then was invoked immediately after the second timeout started, with the result of the call to setTimeout, which is undefined.
I looks like you expected the result you are returning from the callback of the second timeout to be somehow passed as result of the second then, but that's not the way it works.
I have a scenario where I would like to send in 2 or more functions (as parameters) into a handler function, and have that handler function execute each passed function as a callback function for the preceding function.
Here is a general concept of the function I am trying to write:
function functionChain() {
// MAKE SURE WE HAVE AT LEAST 1 PARAMETER
if ( arguments.length < 1 ) { return; }
// for each parameter, call it (as a function)
for ( var i=0; i<arguments.length; i++) {
if ( typeof arguments[i] === 'function' ) {
call arguments[i];
}
}
}
// example
functionChain( function1, function2, function3 );
... so in the code above, each function will be called in succession.
Where I am getting stuck is how to treat each call as a callback when the previous function completes.
The way I would approach this is to have a variable (for simplicity, lets just say a global variable named functionChainComplete), and wait to launch the next function -- and of course, each function I call would set functionChainComplete to true. So, something like this:
// set global var for tracking
var functionChainComplete;
function functionChain() {
// MAKE SURE WE HAVE AT LEAST 1 PARAMETER
if ( arguments.length < 1 ) { return; }
// SET GLOBAL VAR TO FALSE
functionChainComplete = true;
// for each parameter, call it (as a function)
for ( var i=0; i<arguments.length; i++) {
if ( typeof arguments[i] === 'function' ) {
if ( functionChainComplete == true ) {
// call the next function and wait for true again
functionChainComplete = false;
call arguments[i];
} else {
// try again in 50 ms (maybe setTimeout)?
}
}
}
}
function1() {
// do something, and when done, reset functionChainComplete
functionChainComplete = true;
}
function2() {
// do something, and when done, reset functionChainComplete
functionChainComplete = true;
}
function3() {
// do something, and when done, reset functionChainComplete
functionChainComplete = true;
}
// example
functionChain( function1, function2, function3 );
As you can see, the code above does not address the callback piece, and I am not sure where to take it from here - I suspect some sort of recursive function? I am stuck.
Say you have some function, double, that takes an argument, x, and a callback, k
const double = (x, k) =>
k(x * 2)
double(2, console.log) // 4
double(3, console.log) // 6
Now say we want to run it 3 times in a row
const double = (x, k) =>
k(x * 2)
const tripleDouble = (x, k) =>
double(x, y =>
double(y, z =>
double(z, k)))
tripleDouble(2, console.log) // 16
tripleDouble(3, console.log) // 24
But of course we had to statically code each continuation (y => ..., and z => ...). How would we make this work with a variable amount (array) of functions?
const double = (x, k) =>
k(x * 2)
const composek = (...fs) => (x, k) =>
fs.reduce((acc, f) =>
k => acc(x => f(x, k)), k => k(x)) (k)
const foo = composek(double, double, double)
foo(2, console.log) // 16
foo(3, console.log) // 24
This is ripe for some abstraction tho, and introduces my favourite monad, the Continuation Monad.
const Cont = f => ({
runCont: f,
chain: g =>
Cont(k => f(x => g(x).runCont(k)))
})
Cont.of = x => Cont(k => k(x))
const composek = (...fs) => (x, k) =>
fs.reduce((acc,f) =>
acc.chain(x =>
Cont(k => f(x,k))), Cont.of(x)).runCont(k)
const double = (x, k) =>
k(x * 2)
const foo = composek(double, double, double)
foo(2, console.log) // 16
foo(3, console.log) // 24
If you have freedom to change the functions you're chaining, this cleans up a little bit more – here, double has 1 parameter and returns a Cont instead of taking a callback as a second argument
const Cont = f => ({
runCont: f,
chain: g =>
Cont(k => f(x => g(x).runCont(k)))
})
Cont.of = x => Cont(k => k(x))
// simplified
const composek = (...fs) => (x, k) =>
fs.reduce((acc,f) => acc.chain(f), Cont.of(x)).runCont(k)
// simplified
const double = x =>
Cont.of(x * 2)
const foo = composek(double, double, double)
foo(2, console.log) // 16
foo(3, console.log) // 24
Of course if double was actually asynchronous, it would work the same
// change double to be async; output stays the same
const double = x =>
Cont(k => setTimeout(k, 1000, x * 2))
const foo = composek(double, double, double)
foo(2, console.log) // 16
foo(3, console.log) // 24
Something like this? (See comments, but fairly self-explanatory.)
function functionChain() {
var args = arguments;
// MAKE SURE WE HAVE AT LEAST 1 PARAMETER
if ( args.length < 1 ) { return; }
// Start the process
var i = -1;
go();
function go() {
// Pre-increment so we start at 0
++i;
if (i < args.length) {
// We have a next function, do it and continue when we get the callback
args[i](go);
}
}
}
Example:
function functionChain() {
var args = arguments;
// MAKE SURE WE HAVE AT LEAST 1 PARAMETER
if ( args.length < 1 ) { return; }
// Start the process
var i = -1;
go();
function go() {
// Pre-increment so we start at 0
++i;
if (i < args.length) {
// We have a next function, do it and continue when we get the callback
args[i](go);
}
}
}
// Just some functions for an example:
function a(callback) {
console.log("a");
callback();
}
function b(callback) {
console.log("b");
callback();
}
// Note this one is async
function c(callback) {
setTimeout(function() {
console.log("c");
callback();
}, 100);
}
function d(callback) {
console.log("d");
callback();
}
functionChain(a, b, c, d);
That said, one of the reasons for promises is to allow composing possibly-async functions. If your functions returned promises, we'd use the reduce idiom:
function functionChain() {
// Assumes the functions return promises (or at least thenables)
Array.prototype.reduce.call(arguments, function(p, f) {
return p.then(f);
}, Promise.resolve());
}
function functionChain() {
Array.prototype.reduce.call(arguments, function(p, f) {
return p.then(f);
}, Promise.resolve());
}
// Just some functions for an example:
function a(callback) {
return new Promise(function(resolve) {
console.log("a");
resolve();
});
}
function b(callback) {
return new Promise(function(resolve) {
console.log("b");
resolve();
});
}
// Note this one has a delay
function c(callback) {
return new Promise(function(resolve) {
setTimeout(function() {
console.log("c");
resolve();
}, 100);
});
}
function d(callback) {
return new Promise(function(resolve) {
console.log("d");
resolve();
});
}
functionChain(a, b, c, d);
This could be done with nsynjs:
Wrap all slow functions with callbacks into nsynjs-aware wrappers (see wait()),
Put your logic into function as if it was synchronous (see synchronousCode()),
Run that function via nsynjs engine (see nsynjs.run())
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>
<script>
var wait = function (ctx, ms) {
setTimeout(function () {
console.log('firing timeout');
ctx.resume();
}, ms);
};
wait.nsynjsHasCallback = true;
function synchronousCode() {
function function1() {
console.log('in function1');
wait(nsynjsCtx,1000);
};
function function2() {
console.log('in function2');
wait(nsynjsCtx,1000);
};
function function3() {
console.log('in function3');
wait(nsynjsCtx,1000);
};
function functionChain() {
// MAKE SURE WE HAVE AT LEAST 1 PARAMETER
if ( arguments.length < 1 ) return;
for ( var i=0; i<arguments.length; i++) {
//console.log(i,arguments[i]);
if ( typeof arguments[i] === 'function' ) {
arguments[i]();
};
};
};
functionChain(function1,function2,function3);
}
nsynjs.run(synchronousCode,{},function(){
console.log("Synchronous Code done");
})
</script>
See https://github.com/amaksr/nsynjs/tree/master/examples for more examples.