Why does generator's .next need a setTimeout? - javascript

I'm experimenting with ES6's generator functions and yield statements, Example.
function run(generator) {
var itr = generator(resume);
function resume(callbackValue) {
itr.next(callbackValue);
}
itr.next();
}
function* main(resume) {
var result1 = yield add(1, resume);
var data1 = result1;
console.log("add 1 = ", data1)
var data2 = yield add(1, resume);
console.log("add 1 = ", data2);
var data3 = yield add(data1, resume);
console.log("add data1 =", data3);
console.log("total is ", data1 + data2 + data3);
}
function add(num, resume) {
setTimeout(function() {
resume(num + 1);
}, 0);
}
run(main);
I plan on using yield as flow control for asynchronous REST calls, where the request will call next once it has a response, but for now I'm just using a simple adding function. It works as planned which is exciting but resume will only work with in the setTimeout and I'm not sure why.
If it just have:
function add (num, resume) {
resume(num + 1);
}
the interpreter gives me 'Generator is already running'.
There doesn't need to be an actual wait in the time for the timeout, and I also tried a self invoking function, but that didn't help. Why does itr.next() need a timeout?

As other commenters said, you're trying to call resume while the yield expression is still resolving. Asynchronously calling itr.next will allow the yield expression to finish, and then immediately call itr.next when it's done. To get it to work the way you want, just change run:
function run(generator) {
var itr = generator(resume);
function resume(callbackValue) {
setTimeout(function(){
itr.next(callbackValue);
}, 0);
}
itr.next();
}
This is a really fun idea, and it could actually be useful.

Related

Wait for two async functions to finish then continue in Node.js

I'm working on an application in Node.js where I'm calling an async function twice, and assign the value to a global variable.
The issue is that I want to use the result of the two calls to do something else, but this something else doesn't wait for the result to be assigned.
Here's my code:
var a;
var b;
let x = 'abcd';
foo(x).then(data=>{
a = data;
});
x = 'efgh';
foo(x).then(data=>{
b = data;
});
console.log(a + b); // for example
How can I wait for the two functions to finish, before executing a + b?
You can use Promise.all here, to wait for the two promises and then work with their data:
let promises = [];
let x = 'abcd';
promises.push(foo(x))
x = 'efgh';
promises.push(foo(x))
Promise.all(promises).then(([a, b]) => {
console.log(a, b); // for example
});
function foo(d) {
return Promise.resolve({somePaddingData:"", d});
}
As foo returns a Promise you should mark your function as asyncronus with async keyword and wait for the foo function to respond with the await keyword.
async function someFunction(){
let x = 'abcd';
let a = await foo(x);
x = 'efgh';
let b = await foo(x);
console.log(a + b)
}
Instead of using .then() you can use await.
So this:
foo(x).then(data=>{
a = data;
});
would be like this:
a = await foo(x);
Same goes for the other function. This will cause your execution to wait until the functions return.
Notice however that in order to use await you would have to wrap the statement that uses it, or even better the whole block, in a function that is declared as aync.You can find more on how to use async here.
Try this:
//using setInterval to wait for completion
//a function which returns a callback
function foo(x,callback){
//do some computaion on x
callback(x);
};
//to store the response
let result = [];
//calling foo method twice parallely
foo("123",(data)=>{
result.push(data);
});
foo("456",(data)=>{
result.push(data);
});
//waiting till the above functions are done with the execution
//setInterval function will check every 100 ms(can be any value) if the length of the result array is 2
let timer = setInterval(() => {
if (result.length == 2) {
clearInterval(timer);
//prints the two value
console.log(result.join(""))
}
}, 100);

Synchronous way of handling asynchronous function, two level deep

I am looping over an array to update its values using returned value from called function which internally calls an asynchronous function.
I need to handle asynchronous function in synchronous way which is not being directly called. This is replication of scenario.
function condition(){
// Code of this function is not accessible to me.
return new Promise(function(resolve, reject){
setTimeout(function(){
if(parseInt(Math.random() * 100) % 2){
resolve(true);
}
else{
reject(false)
}
}, 1000)
});
}
async function delayIncrease(value){
var choice = await condition();
if(choice) { return ++value; }
else { return --value; }
}
// Start calling functions
dataArr = [1,2,3,4,5];
for(var i in dataArr){
dataArr[i] = delayIncrease(dataArr[i]);
}
If possible, I would like to have the solution in above structure mentioned.
I have achieved the desired result by adding other function and passing "index" + "new_value" as parameters. This function directly modifies original array and produces desired result. Working example.
function condition(){
// Code of this function is not accessible to me.
return new Promise(function(resolve, reject){
setTimeout(function(){
if(parseInt(Math.random() * 100) % 2){
resolve(true);
}
else{
reject(false)
}
}, 1000)
});
}
function delayIncrease(value, index){
condition().then(
function(){ updateData(++value, index) },
function(){ updateData(--value, index) }
)
}
function updateData(value, index){
dataArr[index] = value;
}
dataArr = [1,2,3,4,5];
for(var i in dataArr){
dataArr[i] = delayIncrease(dataArr[i], i);
}
Please provide solution for this requirement in best possible way. Possible solution in Angular 4 way is also appriciated. I thought of writing it in normal JavaScript form as Observables behave nearly same.
I followed this Medium page and http://exploringjs.com
Your condition function does not really fulfill the promise with either true or false, it does randomly fulfill or reject the promise. Instead of branching on a boolean, you will need to catch that "error":
async function delayIncrease(value) {
try {
await condition();
return ++value;
} catch(e) {
return --value;
}
}
You could do something like this:
var condition = async () =>
(parseInt(Math.random() * 100) % 2)
? true
: false
var delayIncrease = async (value) =>
(await condition())
? ++value
: --value
var dataArr = [1, 2, 3, 4, 5];
// Start calling functions
Promise.all(
dataArr.map(
delayIncrease
)
)
.then(
resolve => console.log("results:",resolve)
,reject => console.warn("rejected:",reject)
)
Once something is async you have to make the entire call stack prior to that function async. If a function calls an async function that that function returns an async value and so does the one calling it and calling it and calling it ...
More info on javascript async and why can be found here.
Since the example provided doesn't have any async api's in there you don't need to do it async:
var condition = () =>
(parseInt(Math.random() * 100) % 2)
? true
: false
var delayIncrease = (value) =>
(condition())
? ++value
: --value
var dataArr = [1, 2, 3, 4, 5];
// Start calling functions
dataArr.map(
delayIncrease
)
[update]
When you mutate an array of objects and cosole.log it you may not see the values as they actually were when you log it but you see the values as they are right now (this is a "bug" in console.log).
Consider the following:
var i = -1,arr=[];
while(++i<1){
arr[i]={};
arr[i]["name"+i]=i
}
var process = (index) =>
arr[index]["name"+index]++;
arr.forEach(
(item,index) =>
Promise.resolve(index)
.then(process)
);
console.log("obj at the moment you are looking at it:",arr)
console.log("obj at the moment it is logged:",JSON.stringify(arr))
When you expand obj at the moment you are looking at it you see that name0 property of the first element changed to 1.
However; look at obj at the moment it is logged: and see the actual value of the first element in the array. It has name0 of 0.
You may think that the that code runs asynchronous functions in a synchronous way by mutating the object(s) in an array, but you actually experience a "bug" in console.log

How to do a "for" loop with asynchronous condition in Javascript?

I have this function:
waitForFreeAccnt.prototype.isMemberFree = function () {
var self = this;
self.api.getMemberInfo(function () {
var accType = self.api.connect.accountType;
console.log(accType);
if (accType === 'FREE') {
console.log('it is free');
return true;
} else {
console.log('it is not free');
return false;
}
});
};
I would like to wait till the account is free for up to 10 seconds with something like that:
var test = function () {
for (var start = 1; start < 10; start++) {
var result = self.isMemberFree();
console.log(result);
if (result) {
break;
} else {
self.api.pause(1000);
console.log('waiting');
}
}
};
But it doesn't work because self.api.getMemberInfo is asynch call. This is super frustrating with Javascript. Any other language it would be so simple to do. How do I force the for loop to wait for self.isMemberFree() to finish executing before proceeding with the loop?
Also to note, this is not in browser execution so I don't care about anything hanging.
When dealing with asynchronous code, you need to make use of callbacks. That is, if you want to do a() and b() in order but a() does something asynchronously, then you need to call b() from within a() once a() has a result. So not:
a(); // does something asynchronously
b(); // tries to use a()'s result but it isn't available yet
... but rather
a(b); // pass b to a() and a() will call it when ready
function a(callback) {
triggerAsyncFunction(function(result) {
if (result === something)
callback("a just finished");
});
}
Note that a() doesn't refer to b() by name, it just calls whatever function is passed in as an argument.
So applying that to your code, maybe something like this:
waitForFreeAccnt.prototype.isMemberFree = function (cbf) {
var self = this;
self.api.getMemberInfo(function () {
cbf(self.api.connect.accountType === 'FREE');
});
};
waitForFreeAccnt.prototype.testMemberXTimes = function(maxAttempts, callback) {
var attempts = 0;
var self = this;
(function attempt() {
self.isMemberFree(function(free) {
if (free)
callback(true);
else if (++attempts < maxAttempts)
setTimeout(attempt, 1000);
else
callback(false);
});
)();
};
this.testMemberXTimes(10, function(isFree) {
// the next part of your code here, or called from here
// because at this point we know we've tested up to
// ten times and isFree tells us the result
});
Note that the way I coded getMemberInfo() it is basically doing the same thing yours was, but instead of returning a boolean it is calling the callback function and passing the same boolean value that you were returning. (I've removed the console.log()s to make the code shorter.)
Note also that you could structure the above to use promises, but the end result will be the same.
You could return a Promise
waitForFreeAccnt.prototype.isMemberFree = function () {
return new Promise((reject, resolve)=>
// set a timeout if api call takes too long
var timeout = setTimeout(()=> reject(Error('API timeout')), 10000);
// make api call
this.api.getMemberInfo(()=> {
clearTimeout(timeout);
resolve(this.api.connect.accountType === 'FREE');
});
);
};
Then use it like this
whatever.isMemberFree().then(isFree=> {
if (isFree)
console.log('it is free');
else
console.log('it is not free');
})
// handle timeout or other errors
.catch(err=> {
console.log(err.message);
});
Building on naomik's answer, if you do it that way you can pretty easily use a for loop with it, using the (most likely) upcoming async/await feature - though it's not part of ES2015.
// Note "async" here! That will make "await" work. It makes the function
// return a promise, which you'll be able to either "await" or
// "test().then" later.
var test = async function () {
for (var start = 1; start < 10; start++) {
// Right here we're using "await" - it makes JavaScript *wait* for
// the promise that comes from self.isMemberFree() to be finished.
// It's really handy because you can use it in loops like "for" and
// "while" without changing the flow of your program!
var result = await self.isMemberFree();
console.log(result);
if (result) {
break;
} else {
self.api.pause(1000);
console.log('waiting');
}
}
};
For now you'll need to use a transpiler like Babel or Traceur before you can really use async/await, though. It's only supported in Microsoft Edge 14 right now.
And a big emphasis that what is returned from test() isn't whatever you directly return from inside it. If I do this:
var test = async function() { return 15; };
var result = test();
I'm not going to get 15 - I'll get a promise that will resolve as 15:
result.then(function(res) {
console.log(res); // 15
});
// or, use an async function again:
var main = async function() {
console.log(await res); // 15
};
main();
I don't have my work laptop today because it is Sunday, I'm coding this on sublime. Apologise if the syntax is a bit off.
To solve your problem I would recommend changing isMemberFree() to take in a callback function. This is because isMemberFree is async, and you will need a way to report the result after it has done the work.
Then change test function to use setTimeout API to wait a second.
Wrap the function call for isMemberFree() to be in a nested function and call it recursively, that way you'll have synchronize control over the async calls.
Look at the coding example:
waitForFreeAccnt.prototype.isMemberFree = function (done) {
var self = this;
self.api.getMemberInfo(function () {
var accType = self.api.connect.accountType;
console.log(accType);
if (accType === 'FREE') {
console.log('it is free');
return done(null, true);
} else {
console.log('it is not free');
return done(null, false);
}
});
};
var test = function () {
var testMembership = function(waitAttempt, isFree) {
if (isFree) {
return;
}
else if (waitAttempt > 10) {
// wait exceeded, do something.
return;
}
setTimeout(function() {
self.isMemberFree(function(err, isFree) {
testMembership(waitAttempt+=1, isFree);
});
}, /*total milliseconds in 1sec=*/1000);
}
testMembership(/*WaitAttempts=*/0, /*isFree=*/false);
};
What the above code does is that, presumably something has already been done to the member's account and now test function is called. So it waits for 1 second, then call isMemberFree function, this happens recursively until either isMemberFree() returns true OR the 10 seconds wait has been exceeded.

ES6 generators: transforming callbacks to iterators

I'm experimenting with ES6 generators with the help of babel, and I have trouble understand how (or if!) I can effectively use callback based async function to output an iterator.
Let's say I want be able to write a function that takes a number of urls, asynchronously download them and returns them as soon as they are downloaded.
I would like to be able to write something like the following:
let urls = ['http://www.google.com', 'http://www.stackoverflow.com' ];
for ( {url, data} of downloadUrls(urls) ) {
console.log("Content of url", url, "is");
console.log(data);
}
How can I implement downloadUrls ?
Ideally I would like to be able to write the following:
var downloadUrls = function*(urls) {
for( let url of urls ) {
$.ajax(url).done( function(data) {
yield data;
});
}
};
This of course doesn't work, since ``yield'' is being invoked inside a callback and not directly inside the generator.
I can find many examples online of people trying the same, they are either not much transparent), require enabling browser/node flags, or use node-specific features/libraries.
The library closest to what I need seems to be task.js, but I'm unable to have even the simplest example run on current Chrome.
Is there a way to get the intended behaviour using standard and current features , (With current I mean usable with transpilers like babel, but without the need to enable extra flags on the browser) or do I have to wait for async/await ?
2019 update
Yielding via callbacks is actually pretty simple. Since you can only call yield directly from the generator function* where it appears (and not from callbacks), you need to yield a Promise instead, which will be resolved from the callback:
async function* fetchUrls(urls) {
for (const url of urls)
yield new Promise((resolve, reject) => {
fetch(url, { mode: 'no-cors' }).then(response => resolve(response.status));
});
}
(async function main() {
const urls = ['https://www.ietf.org/rfc/rfc2616.txt', 'https://www.w3.org/TR/PNG/iso_8859-1.txt'];
// for-await-of syntax
for await (const status of fetchUrls(urls))
console.log(status);
}());
If the example doesn't work in the browser (it my return 0 instead of 200 due to Cross Origin Read Blocking), try it live on repl.it.
Is there a way to get the intended behaviour using standard and current features
Yes, use promises and generators. Many promise libraries, and some standalone ones, feature the use of generator "coroutines".
But notice that you cannot mix iteration with asynchrony, you can use generators for either only. Your example seems to confuse them a bit - it looks like you expect that for ( {url, data} of downloadUrls(urls) ) { loop to work synchronously, which cannot work.
do I have to wait for async/await?
No, you don't have to wait, Babel already supports them!
Here is a clean way to use a generator / iterator to flatten asynchronous code which works for me in node.js:
var asyncProcedureGenerator1 = function*() {
var it = yield(0); //get a reference to the iterator
try {
var a = yield (asyncPart1.bind(it))(0); //call the function, set this = it
var b = yield (asyncPart2.bind(it))(a);
var c = yield (asyncPart3.bind(it))(b);
console.log("c = ", c);
}
catch(err)
{
console.log("Something went wrong: ", err);
}
};
var runAsyncGenerator = function(generator) {
var asyncProcedureIterator = generator(); //create an iterator
asyncProcedureIterator.next(); //start the iterator
asyncProcedureIterator.next(asyncProcedureIterator); //pass a reference of the iterator to itself
}
var asyncPart1 = function(param1) {
var it = this; //the iterator will be equal to this.
console.log("Starting asyncPart1 with param1 = ", param1);
setTimeout(function() {
console.log("Done with asyncPart1");
var returnValue = 42 + param1;
console.log("asyncPart1 returned ", returnValue);
it.next(returnValue); //when we are done, resume the iterator which has yielded to us.
},2000);
};
var asyncPart2 = function(param1) {
var it = this; //the iterator will be equal to this.
console.log("Starting asyncPart2 with param1 = ", param1);
setTimeout(function() {
console.log("Done with asyncPart2");
var returnValue = param1 / 2;
console.log("asyncPart2 returned ", returnValue);
//it.throw("Uh oh.");
it.next(returnValue);
},2000);
};
var asyncPart3 = function(param1) {
var it = this; //the iterator will be equal to this.
console.log("Starting asyncPart3 with param1 = ", param1);
setTimeout(function() {
console.log("Done with asyncPart3");
var returnValue = param1 / 3;
console.log("asyncPart3 returned ", returnValue);
it.next(returnValue);
},2000);
};
runAsyncGenerator(asyncProcedureGenerator1);
The idea is to run the generator, creator an iterator, and then pass a reference of that iterator to itself.
Then the iterator can call asynchronous functions (with yield) and pass them a reference to itself which allows those functions to either return success and resume the execution by calling iterator.next(result) or failure by calling iterator.throw(error).
I just came up with this pattern, so there may be some gotchas I haven't found yet, but it seems to work and allows very flat code with minimal additions.

JavaScript Asynch Callback - How to await the endresult

I have the following situation (see also jsFiddle -> http://jsfiddle.net/sMuWK/):
function CallBackStringHandler() {
this.callback = function(){return null};
};
CallBackStringHandler.prototype.doTheMagic = function(callback) {
var result = callback.call(this);
if(result == null)
alert("Nothing to handle yet...");
else
alert("End the result is: \n\n" + result);
};
function Action(){
var result = null;
var max = 10;
var index = 0;
var processor = setInterval(function(){
if(index <= max){ //Processing step
if(result == null)
result = "" + index;
else
result += index;
index++;
} else { //Done
clearInterval(processor);
alert(result);
}
},10);
return result;
};
function Run(){
var handler = new CallBackStringHandler();
handler.doTheMagic(Action);
};
Run();
A script (a jQuery plugin) allows you to specify a callback that has to return a string.
This string will be handled by this script.
So far so good.
For the sake of performance and keeping my page responsive, I want to build this string in a multi-threaded way. Since this is not a web standard yet, I simulate this with the help of setInterval.
Now I know that the essence of doing things this way is not waiting for the results.
But I can't think of a way of keeping things responsive and fast and return the full result to the handler.
So the end result (in this example) should show: 012345678910.
Any help/clues would be appreciated.
Cheers, another nerd.
You need to turn it the other way around. Action is not a callback, it does not consume an asynchronous result but it produces it. doTheMagic on the other hand is the callback, as it consumes the result (by alerting the result).
Thus, instead of passing Action as a "callback" to doTheMagic, you should be passing doTheMagic as a callback to Action.
function Run() {
var handler = new CallBackStringHandler();
Action(function(result) {
handler.doTheMagic(result);
});
// or, alternatively: (only in modern browsers supporting Function.bind)
Action(handler.doTheMagic.bind(handler));
};
Make Action accept a callback argument and call it when it's done. Finally, let doTheMagic just receive the result. I forked your fiddle, have a look!
Note: You won't get multi-threading using setInterval, it will still run in the same browser thread as the rest of your script. If you truly need to do some serious heavy lifting, you may want to use a web worker.
For most cases such as just concatenating a string like you're doing, this is overkill. Workers live in a completely separate environment and you can only communicate with them through messages, which adds quite a bit of complexity to your application. Make sure to do a good amount of testing and benchmarking before deciding that you really need a multi-threaded approach!
So to for a final answer I kinda resolved it this way (fork here):
function CallBackStringHandlerBy3rdParty() {};
CallBackStringHandlerBy3rdParty.prototype.doMagic = function(callback) {
var result = callback.call(this);
alert(result);
};
CallBackStringHandlerBy3rdParty.prototype.doMyOwnMagic = function(result) {
if(result.isComplete) {
this.doMagic(function(){return result.value;});
} else {
var that = this;
result.value += 1;
if(result.value < 10)
setTimeout(function(){that.doMyOwnMagic(result);},10);
else {
result.isComplete = true;
this.doMyOwnMagic(result);
}
}
};
function Run(){
var handler = new CallBackStringHandlerBy3rdParty();
var result = {};
result.value = 0;
result.isComplete = false;
handler.doMyOwnMagic(result);
};
Run();
Cheers!

Categories

Resources