How to work around promises when using continuation-local-storage? - javascript

continuation-local-storage seems to be used, also in context of express.
Yet the very basic usage does not work for me, since the context is completely lost!
var createNamespace = require('continuation-local-storage').createNamespace;
var session = createNamespace('my session');
async function doAsync() {
console.log('Before getting into the promise: ' + session.get('test'));
await Promise.resolve();
console.log('*in* the promise: ' + session.get('test'));
}
session.run(() => {
session.set('test', 'peekaboo');
doAsync();
});
results in:
$ node server_test.js
Before getting into the promise: peekaboo
*in* the promise: undefined
Have I done something completely wrong or is the CLS simply broken?
Or is the library broken?
If it's not meant to work with promises, are there other concepts that work as a threadLocal storage to implement multi-tenancy in a proper way?

cls-hooked seems to be working fine, though the library (as the previous one) were last updated two years ago...
If someone has some other more robust way to implement a thread-local state for multi-tenancy please share!

You are trying to maintain a shared state across multiple functions which are then executes asynchronously. That is a very common case in JS, and the language itself provides a very simple, yet powerful mechanism for that: You can access variables from an outer function from an inner function, even if the outer function already finished its execution:
(function closured() {
let shared = { /*...*/ };
function inner() {
console.log( shared );
}
setTimeout(inner);
})();
Now while that works, it doesn't scale that well for larger applications: All functions accessing that state have to be inside of one function, and therefore that file gets really blown up. The libraries you are using are trying to resolve that, they also use closures to maintain the state across asynchronous calls: When you register an async callback the state is stored in a function, then the callback itself gets wrapped into a callback that restores the state:
let state;
function setTimeoutWithState(cb, time) {
const closured = state;
setTimeout(() => {
state = closured;
cb();
}, time);
}
state = 1;
setTimeoutWithState(() => console.log(state), 1000);
state = 2;
setTimeoutWithState(() => console.log(state), 500);
Now the library just has to wrap every async callback that way, and then you can maintain the state easily, isn't that great? Well for sure it is, but adding code to every callback does have it's cost (as JS heavily utilizes callbacks).

Related

JavaScript - Attach context to async function call?

The bounty expires in 19 hours. Answers to this question are eligible for a +100 reputation bounty.
David Callanan wants to draw more attention to this question.
Synchronous function call context
In JavaScript, it's easy to associate some context with a synchronous function call by using a stack in a global scope.
// Context management
let contextStack = [];
let context;
const withContext = (ctx, func) {
contextStack.push(ctx);
context = ctx;
try {
return func();
} finally {
context = contextStack.pop();
}
};
// Example
const foo = (message) => {
console.log(message);
console.log(context);
};
const bar = () => {
withContext("calling from bar", () => foo("hello"));
};
This allows us to write context-specific code without having to pass around a context object everywhere and have every function we use depend on this context object.
This is possible in JavaScript because of the guarantee of sequential code execution, that is, these synchronous functions are run to completion before any other code can modify the global state.
Generator function call context
We can achieve something similar with generator functions. Generator functions give us an opportunity to take control just before conceptual execution of the generator function resumes. This means that even if execution is suspended for a few seconds (that is, the function is not run to completion before any other code runs), we can still ensure that there is an accurate context attached to its execution.
const iterWithContext = function* (ctx, generator) {
// not a perfect implementation
let iter = generator();
let reply;
while (true) {
const { done, value } = withContext(ctx, () => iter.next(reply));
if (done) {
return;
}
reply = yield value;
}
};
Question: Async function call context?
It would also be very useful to attach some context to the execution of an async function.
const timeout = (ms) => new Promise(res => setTimeout(res, ms));
const foo = async () => {
await timeout(1000);
console.log(context);
};
const bar = async () => {
await asyncWithContext("calling from bar", foo);
};
The problem is, to the best of my knowledge, there is no way of intercepting the moment before an async function resumes execution, or the moment after the async function suspends execution, in order to provide this context.
Is there any way of achieving this?
My best option right now is to not use async functions, but to use generator functions that behave like async functions. But this is not very practical as it requires the entire codebase to be written like this.
Background / Motivation
Using context like this is incredibly valuable because the context is available deep down the call-stack. This is especially useful if a library needs to call an external handler such that if the handler calls back to the library, the library will have the appropriate context. For example, I'd imagine React hooks and Solid.js extensively use context in this way under-the-hood. If not done this way, the programmer would have to pass a context object around everywhere and use it when calling back to the library, which is both messy and error-prone. Context is a way to neatly "curry" or abstract away a context object from function calls, based on where we are in the call stack. Whether it is good practice or not is debatable, but I think we can agree that it's something library authors have chosen to do. I would like to extend the use of context to asynchronous functions, which are supposed to conceptually behave like synchronous functions when it comes to the execution flow.
As far as I know ECMA has no specification for "contexts" (regardless if it's a normal or async function).
Therefore the solution you posted for normal functions is already a hack.
As per ECMA standard, there is no JavaScript based API to hook await in order to do a generator like trick. So you have to rely on (environment based) hacks.
These hacks may highly depend on the environment you're using.
JavaScript Only (requires async stack traces)
A solution which is purly based async stack traces is the following one.
Since nearly every JavaScript interpreter is based on V8 this works on nearly every use case.
const kContextIdFunctionPrefix = "__context_id__";
const kContextIdRegex = new RegExp(`${kContextIdFunctionPrefix}([0-9]+)`);
let contextIdOffset = 0;
function runWithContextId(target, ...args) {
const contextId = ++contextIdOffset;
let proxy;
eval(`proxy = async function ${kContextIdFunctionPrefix}${contextId}(target, ...args){ return await target.call(this, ...args); }`);
return proxy.call(this, target, ...args);
}
function getContextId() {
const stack = new Error().stack.split("\n");
for(const frame of stack) {
const match = frame.match(kContextIdRegex);
if(!match) {
continue;
}
const id = parseInt(match[1]);
if(isNaN(id)) {
console.warn(`Context id regex matched, but failed to parse context id from ${match[1]}`);
continue;
}
return id;
}
console.log(new Error().stack)
throw new Error("getContextId() called without providing a context (runWithContextId(...))");
}
A simple demo:
async function main() {
const target = async () => {
const contextId = getContextId();
console.log(`Context Id: ${contextId}`);
await new Promise(resolve => setTimeout(resolve, 1000));
console.log(`Context Id (After await): ${getContextId()} (before: ${contextId})`);
return contextId;
};
const contextIdA = runWithContextId(target);
const contextIdB = runWithContextId(target);
// Note: We're first awaiting the second call!
console.log(`Invoke #2 context id: ${await contextIdB}`);
console.log(`Invoke #1 context id: ${await contextIdA}`);
}
main();
This solution leverages stack traces in order to identify a context id. Traversing the (sync and async) stack trace and using dynamically generated functions with special names allows to pass a special value (a number in this instance).
NodeJS (AsyncLocalStorage)
NodeJS offers a way for Asynchronous context tracking:
https://nodejs.org/api/async_context.html#class-asynclocalstorage
It should be possible to build an async context by using AsyncLocalStorage.
Using a transpiler
You might want to use a transpiler (like babel or typescript) which convert async functions to generator functions on the fly.
Using a transpiler allows you to even write a plugin for implementing async contexts based on generator functions.

Better way to "loop" promises

This is a post that might come across as quite conceptual, since I first start with a lot of pseudo code. - At the end you'll see the use case for this problem, though a solution would be a "tool I can add to my tool-belt of useful programming techniques".
The problem
Sometimes one might need to create multiple promises, and either do something after all promises have ended. Or one might create multiple promises, based on the results of the previous promises. The analogy can be made to creating an array of values instead of a single value.
There are two basic cases to be considered, where the number of promises is indepedented of the result of said promises, and the case where it is depedent. Simple pseudo code of what "could" be done.
for (let i=0; i<10; i++) {
promise(...)
.then(...)
.catch(...);
}.then(new function(result) {
//All promises finished execute this code now.
})
The basically creates n (10) promises, and the final code would be executed after all promises are done. Of course the syntax isn't working in javascript, but it shows the idea. This problem is relativelly easy, and could be called completely asynchronous.
Now the second problem is like:
while (continueFn()) {
promise(...)
.then(.. potentially changing outcome of continueFn ..)
.catch(.. potentially changing outcome of continueFn ..)
}.then(new function(result) {
//All promises finished execute this code now.
})
This is much more complex, as one can't just start all promises and then wait for them to finish: in the end you'll have to go "promise-by-promise". This second case is what I wish to figure out (if one can do the second case you can also do the first).
The (bad) solution
I do have a working "solution". This is not a good solution as can probably quickly be seen, after the code I'll talk about why I dislike this method. Basically instead of looping it uses recursion - so the "promise" (or a wrapper around a promise which is a promise) calls itself when it's fulfilled, in code:
function promiseFunction(state_obj) {
return new Promise((resolve, reject) => {
//initialize fields here
let InnerFn = (stateObj) => {
if (!stateObj.checkContinue()) {
return resolve(state_obj);
}
ActualPromise(...)
.then(new function(result) {
newState = stateObj.cloneMe(); //we'll have to clone to prevent asynchronous write problems
newState.changeStateBasedOnResult(result);
return InnerFn(newState);
})
.catch(new function(err) {
return reject(err); //forward error handling (must be done manually?)
});
}
InnerFn(initialState); //kickstart
});
}
Important to note is that the stateObj should not change during its lifetime, but it can be really easy. In my real problem (which I'll explain at the end) the stateObj was simply a counter (number), and the if (!stateObj.checkContinue()) was simply if (counter < maxNumber).
Now this solution is really bad; It is ugly, complicated, error prone and finally impossible to scale.
Ugly because the actual business logic is buried in a mess of code. It doesn't show "on the can" that is actually simply doing what the while loop above does.
Complicated because the flow of execution is impossible to follow. First of all recursive code is never "easy" to follow, but more importantly you also have to keep in mind thread safety with the state-object. (Which might also have a reference to another object to, say, store a list of results for later processing).
It's error prone since there is more redundancy than strictly necessary; You'll have to explicitly forward the rejection. Debugging tools such as a stack trace also quickly become really hard to look through.
The scalability is also a problem at some points: this is a recursive function, so at one point it will create a stackoverflow/encounter maximum recursive depth. Normally one could either optimize by tail recursion or, more common, create a virtual stack (on the heap) and transform the function to a loop using the manual stack. In this case, however, one can't change the recursive calls to a loop-with-manual-stack; simply because of how promise syntax works.
The alternative (bad) solution
A colleague suggested an alternative approach to this problem, something that initially looked much less problematic, but I discarded ultimatelly since it was against everything promises are meant to do.
What he suggested was basically looping over the promises as per above. But instead of letting the loop continue there would be a variable "finished" and an inner loop that constantly checks for this variable; so in code it would be like:
function promiseFunction(state_obj) {
return new Promise((resolve, reject) => {
while (stateObj.checkContinue()) {
let finished = false;
let err = false;
let res = null;
actualPromise(...)
.then(new function(result) {
res = result;
finished = true;
})
.catch(new function(err) {
res = err;
err = true;
finished = true;
});
while(!finished) {
sleep(100); //to not burn our cpu
}
if (err) {
return reject(err);
}
stateObj.changeStateBasedOnResult(result);
}
});
}
While this is less complicated, since it's now easy to follow the flow of execution. This has problems of its own: not for the least that it's unclear when this function will end; and it's really bad for performance.
Conclusion
Well this isn't much to conclude yet, I'd really like something as simple as in the first pseudo code above. Maybe another way of looking at things so that one doesn't have the trouble of deeply recursive functions.
So how would you rewrite a promise that is part of a loop?
The real problem used as motivation
Now this problem has roots in a real thing I had to create. While this problem is now solved (by applying the recursive method above), it might be interesting to know what spawned this; The real question however isn't about this specific case, but rather on how to do this in general with any promise.
In a sails app I had to check a database, which had orders with order-ids. I had to find the first N "non existing order-ids". My solution was to get the "first" M products from the database, find the missing numbers within it. Then if the number of missing numbers was less than N get the next batch of M products.
Now to get an item from a database, one uses a promise (or callback), thus the code won't wait for the database data to return. - So I'm basically at the "second problem:"
function GenerateEmptySpots(maxNum) {
return new Promise((resolve, reject) => {
//initialize fields
let InnerFn = (counter, r) => {
if (r > 0) {
return resolve(true);
}
let query = {
orderNr: {'>=': counter, '<': (counter + maxNum)}
};
Order.find({
where: query,
sort: 'orderNr ASC'})
.then(new function(result) {
n = findNumberOfMissingSpotsAndStoreThemInThis();
return InnerFn(newState, r - n);
}.bind(this))
.catch(new function(err) {
return reject(err);
});
}
InnerFn(maxNum); //kickstart
});
}
EDIT:
Small post scriptus: the sleep function in the alternative is just from another library which provided a non-blocking-sleep. (not that it matters).
Also, should've indicated I'm using es2015.
The alternative (bad) solution
…doesn't actually work, as there is no sleep function in JavaScript. (If you have a runtime library which provides a non-blocking-sleep, you could just have used a while loop and non-blocking-wait for the promise inside it using the same style).
The bad solution is ugly, complicated, error prone and finally impossible to scale.
Nope. The recursive approach is indeed the proper way to do this.
Ugly because the actual business logic is buried in a mess of code. And error-prone as you'll have to explicitly forward the rejection.
This is just caused by the Promise constructor antipattern! Avoid it.
Complicated because the flow of execution is impossible to follow. Recursive code is never "easy" to follow
I'll challenge that statement. You just have to get accustomed to it.
You also have to keep in mind thread safety with the state-object.
No. There is no multi-threading and shared memory access in JavaScript, if you worry about concurrency where other stuff affects your state object while the loop runs that will a problem with any approach.
The scalability is also a problem at some points: this is a recursive function, so at one point it will create a stackoverflow
No. It's asynchronous! The callback will run on a new stack, it's not actually called recursively during the function call and does not carry those stack frames around. The asynchronous event loop already provides the trampoline to make this tail-recursive.
The good solution
function promiseFunction(state) {
const initialState = state.cloneMe(); // clone once for this run
// initialize fields here
return (function recurse(localState) {
if (!localState.checkContinue())
return Promise.resolve(localState);
else
return actualPromise(…).then(result =>
recurse(localState.changeStateBasedOnResult(result))
);
}(initialState)); // kickstart
}
The modern solution
You know, async/await is available in every environment that implemented ES6, as all of them also implemented ES8 now!
async function promiseFunction(state) {
const localState = state.cloneMe(); // clone once for this run
// initialize fields here
while (!localState.checkContinue()) {
const result = await actualPromise(…);
localState = localState.changeStateBasedOnResult(result);
}
return localState;
}
Let’s begin with the simple case: You have N promises that all do some work, and you want to do something when all the promises have finished. There’s actually a built-in way to do exactly that: Promise.all. With that, the code will look like this:
let promises = [];
for (let i=0; i<10; i++) {
promises.push(doSomethingAsynchronously());
}
Promise.all(promises).then(arrayOfResults => {
// all promises finished
});
Now, the second call is a situation you encounter all the time when you want to continue doing something asynchronously depending on the previous asynchronous result. A common example (that’s a bit less abstract) would be to simply fetch pages until you hit the end.
With modern JavaScript, there’s luckily a way to write this in a really readable way: Using asynchronous functions and await:
async function readFromAllPages() {
let shouldContinue = true;
let pageId = 0;
let items = [];
while (shouldContinue) {
// fetch the next page
let result = await fetchSinglePage(pageId);
// store items
items.push.apply(items, result.items);
// evaluate whether we want to continue
if (!result.items.length) {
shouldContinue = false;
}
pageId++;
}
return items;
}
readFromAllPages().then(allItems => {
// items have been read from all pages
});
Without async/await, this will look a bit more complicated, since you need to manage all this yourself. But unless you try to make it super generic, it shouldn’t look that bad. For example, the paging one could look like this:
function readFromAllPages() {
let items = [];
function readNextPage(pageId) {
return fetchSinglePage(pageId).then(result => {
items.push.apply(items, result.items);
if (!result.items.length) {
return Promise.resolve(null);
}
return readNextPage(pageId + 1);
});
}
return readNextPage(0).then(() => items);
}
First of all recursive code is never "easy" to follow
I think the code is fine to read. As I’ve said: Unless you try to make it super generic, you can really keep it simple. And naming also helps a lot.
but more importantly you also have to keep in mind thread safety with the state-object
No, JavaScript is single-threaded. You doing things asynchronously but that does not necessarily mean that things are happening at the same time. JavaScript uses an event loop to work off asynchronous processes, where only one code block runs at a single time.
The scalability is also a problem at some points: this is a recursive function, so at one point it will create a stackoverflow/encounter maximum recursive depth.
Also no. This is recursive in the sense that the function references itself. But it will not call itself directly. Instead it will register itself as a callback when an asynchronous process finishes. So the current execution of the function will finish first, then at some point the asynchronous process finishes, and then the callback will eventually run. These are (at least) three separate steps from the event loop, which all run independently from another, so you do no have a problem with recursion depth here.
The crux of the matter seems to be that "the actual business logic is buried in a mess of code".
Yes it is ... in both solutions.
Things can be separated out by :
having an asyncRecursor function that simply knows how to (asynchronously) recurse.
allowing the recursor's caller(s) to specify the business logic (the terminal test to apply, and the work to be performed).
It is also better to allow caller(s) to be responsible for cloning the original object rather than resolver() assuming cloning always to be necessary. The caller really needs to be in charge in this regard.
function asyncRecursor(subject, testFn, workFn) {
// asyncRecursor orchestrates the recursion
if(testFn(subject)) {
return Promise.resolve(workFn(subject)).then(result => asyncRecursor(result, testFn, workFn));
// the `Promise.resolve()` wrapper safeguards against workFn() not being thenable.
} else {
return Promise.resolve(subject);
// the `Promise.resolve()` wrapper safeguards against `testFn(subject)` failing at the first call of asyncRecursor().
}
}
Now you can write your caller as follows :
// example caller
function someBusinessOrientedCallerFn(state_obj) {
// ... preamble ...
return asyncRecursor(
state_obj, // or state_obj.cloneMe() if necessary
(obj) => obj.checkContinue(), // testFn
(obj) => somethingAsync(...).then((result) => { // workFn
obj.changeStateBasedOnResult(result);
return obj; // return `obj` or anything you like providing it makes a valid parameter to be passed to `testFn()` and `workFn()` at next recursion.
});
);
}
You could theoretically incorporate your terminal test inside the workFn but keeping them separate will help enforce the discipline, in writers of the business-logic, to remember to include a test. Otherwise they will consider it optional and sure as you like, they will leave it out!
Sorry, this doesn't use Promises, but sometimes abstractions just get in the way.
This example, which builds from #poke's answer, is short and easy to comprehend.
function readFromAllPages(done=function(){}, pageId=0, res=[]) {
fetchSinglePage(pageId, res => {
if (res.items.length) {
readFromAllPages(done, ++pageId, items.concat(res.items));
} else {
done(items);
}
});
}
readFromAllPages(allItems => {
// items have been read from all pages
});
This has only a single depth of nested functions. In general, you can solve the nested callback problem without resorting to a subsystem that manages things for you.
If we drop the parameter defaults and change the arrow functions, we get code that runs in legacy ES3 browsers.

How can I use Promises and Cursors together while remaining pure?

In my classes, I've been learning indexeddb and how to handle asynchronous-ity. In the interest of stretching myself and learning more, I've been trying to use the functional paradigm in my code.
I'd been using cursors before but I've realized that my code isn't completely immutable or stateless, which bothers me. I was wondering if there was a way to use cursors without having to resort to pushing elements to an array.
Currently, I'm using something like:
async function getTable(){
return new Promise(function(resolve, reject){
const db = await connect();
const transaction = await db.transaction(["objectStore"], "readonly");
const store = await transaction.objectStore("objectStore");
var myArray = [];
store.openCursor().onsuccess = function(evt) {
var cursor = evt.target.result;
if (cursor) {
myArray.push(cursor.value);
//I don't want to use push, because it's impure. See link:
cursor.continue();
} else {
resolve(myArray);
}
}
}
//link: https://en.wikipedia.org/wiki/Purely_functional_programming
And it works fine. But it's not pure, and it uses push. I'd like to learn another way to do it, if there is one.
Thank you!
You could do several things in the spirit of functional programming, but it is probably not worth it in JavaScript.
For example, to implement immutability arrays, at least in spirit, you simply create and return a new array everytime you want to add an element to an array. I think if I recall my Scheme correctly the function was called cons.
function push(array, newValue) {
const copy = copyArray(array);
copy.push(newValue);
return copy;
}
function copyArray(array) {
const copy = [];
for(const oldValue of array) {
copy.push(oldValue);
}
return copy;
}
// Fancy spread operator syntax implementation if you are so inclined
function copyArray2(inputArray) {
return [...inputArray];
}
Now instead of mutating the input array, you are creating a modified copy of it. Keep in mind this is absolutely horrific performance, and you probably never ever ever want to do this in a real app.
You could take this further probably and use some stack based approach. Again, this is hilariously bad, but it would be something that basically creates a push function that returns a function. The stack increases in size as you append items, and then when you unwind it, it unwinds into an array of values.
My second point is that you can avoid this array building entirely by using newer, less-well-documented features of indexedDB. Specifically, IDBObjectStore.prototype.getAll. This function would create the array for you, opaquely, and provided that it is opaque, you will never know about any FP anti-patterns hidden within its abstraction, and therefore are not breaking the rules.
function getTable(){
return new Promise(function(resolve, reject){
const db = await connect();
const transaction = await db.transaction(["objectStore"], "readonly");
const store = await transaction.objectStore("objectStore");
const request = store.getAll();
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
}
}
My third point is simple, use db.transaction("objectStore", "readonly"); instead of db.transaction(["objectStore"], "readonly");. The array parameter is optional, and it is preferable to keep it simple.
My fourth point is simple, use db.transaction("objectStore"); instead of db.transaction(["objectStore"], "readonly");. "readonly" is the default mode of a transaction, so there is no need to specify it. The purpose of your code is sufficiently clearly communicated by not specifying the parameter, and omitting the parameter is less verbose.
My fifth point is your use of the async specifier(?) in the function definition. You don't need to use it here. You have a synchronous function returning an instance of a Promise. If anything, specifying async leads to increased confusion about what your code is doing. Instead, you probably want to be using the async qualifier when using the function.
My sixth point is that you are violating some FP principles in your call to connect(). What is connect connecting to? Implied global state. This is quite the violation of the spirit of functional programming. Therefore, your connect parameters must be parameters to the function itself, so that you do not rely on the implied knowledge of which database is used.
My seventh point is that you are using a database. Functional programmers have so many problems with databases, or any I/O or interaction with the outside world, they seem to like to pretend there is no such thing. Therefore, you probably shouldn't be using a database at all if you want to use functional approach.
My eight point is that connecting within the promise (calling and awaiting connect) is definitively an anti-pattern. The goal is to chain promises, so that one starts after the other. Either the caller has to call connect and then call getTable, or getTable has to call connect and then do the rest of the promise work.
My ninth point is that I am not even sure how this executes. The executor function that you pass to the Promise constructor is not qualified as async. So you are using the await modifier in a non-qualified function. This should be throwing an error. Technically, a promise swallowed exception, meaning that this promise should always be rejecting.
My tenth point is your use of async everywhere. I have no idea what is going on, unless your connect function is returning some kind of wrapper library, but calls to IDBDatabase.prototype.transaction and IDBTransaction.prototype.objectStore are synchronous. It makes no sense why you are awaiting them. They do not return promises.'
My eleventh point is that you are not watching for errors. request.onsuccess is not called back when there is an error. This could lead to your promise never settling. You need to also consider the failure case.
My twelfth point is you seem to missing a closing parentheses for your onsuccess handler function. I am not sure how this code ever is interpreted successfully.

Using generator function next() as a callback in node.js

I'm writing some node.js to interact with sensors over a serial port connection. The code for reading the sensor is asynchronous, naturally. In my control code, though, I need to read a sensor, do something based on the value, read again, do something else, etc. To do this, I'm using code like the following self-contained test:
var main = new Main();
main.next();
function* Main()
{
var reading = yield readSensor(this.next.bind(this));
console.log(reading);
var reading = yield readSensor(this.next.bind(this));
console.log(reading);
}
function readSensor(callback)
{
// simulate asynchrounous callback from reading sensor
setTimeout(function sensorCallback() { callback('foo'); }, 100);
}
So, my sequential control code is in a generator which yields to readSensor() when it needs to get a reading. When the sensor reading is done, it calls the callback, and control returns to the main code. I'm doing it this way because I may need to read from various sensors in different orders depending on previous readings. So, here's the questionable part: I pass this.next.bind(this) as a callback to the asynchronous read function. The code seems to work when generators are enabled (--harmony_generators), but I am wondering if there are pitfalls here that I am missing. I'm relatively new to JS, so don't be afraid to point out the obvious :)
I haven't studied ES6 generators in depth, but having a generator pass its own .next to another function as a callback doesn't sit well with me. If anything, it could create a situation where readSensor fails and you have no way to handle the failure, ending up in a deadlock.
I suggest modifying or wrapping readSensor to return a promise, and then using the technique outlined in this article.
That would allow you to write code like this (verified working in Node v0.12.0):
var Promise = require('q');
var main = async(function* () {
var reading = yield readSensor();
console.log(reading);
reading = yield readSensor();
console.log(reading);
});
main();
function readSensor() {
return Promise.delay(2000).thenResolve(Math.random() * 100);
}
/***********************************************************
* From here down, *
* boilerplate async() function from article linked above *
***********************************************************/
function async(makeGenerator){
return function () {
var generator = makeGenerator.apply(this, arguments);
function handle(result){
// result => { done: [Boolean], value: [Object] }
if (result.done) return Promise.resolve(result.value);
return Promise.resolve(result.value).then(function (res){
return handle(generator.next(res));
}, function (err){
return handle(generator.throw(err));
});
}
try {
return handle(generator.next());
} catch (ex) {
return Promise.reject(ex);
}
}
}
As loganfsmyth notes below, Q already provides a Q.async() method that provides the functionality of this async() function, and possibly other promise libraries do as well.
So, here's the questionable part: I pass this.next.bind(this) as a callback to the asynchronous read function. The code seems to work when generators are enabled
No, that doesn't work. Generators cannot be constructed using new like you do. From the spec:
If the generator was invoked using [[Call]], the this binding will have already been initialized in the normal manner. If the generator was invoked using [[Construct]], the this bind is not initialized and any references to this within the FunctionBody will produce a ReferenceError exception.
Generator functions be invoked with new (see §9.2.3, with derived for [[ConstructorKind]]), but they do not construct an instance.
When the sensor reading is done, […] control returns to the main code.
That's a clever idea indeed. It has been explored before, see Understanding code flow with yield/generators or this article. Many libraries support this, especially combined with promises.
I suggest you use one of these libraries, your current code isn't really stable (will break with full ES6 support) and also seems to lacks error handling.

Wait for an async function to return in Node.js

Supposed, I have a async function in Node.js, basically something such as:
var addAsync = function (first, second, callback) {
setTimeout(function () {
callback(null, first + second);
}, 1 * 1000);
};
Now of course I can call this function in an asynchronous style:
addAsync(23, 42, function (err, result) {
console.log(result); // => 65
});
What I am wondering about is whether you can make it somehow to call this function synchronously. For that, I'd like to have a wrapper function sync, which basically does the following thing:
var sync = function (fn, params) {
var res,
finished = false;
fn.call(null, params[0], params[1], function (err, result) {
res = result;
finished = true;
});
while (!finished) {}
return res;
};
Then, I'd be able to run addAsync synchronously, by calling it this way:
var sum = sync(addAsync, [23, 42]);
Note: Of course you wouldn't work using params[0] and params[1] in reality, but use the arguments array accordingly, but I wanted to keep things simple in this example.
Now, the problem is, that the above code does not work. It just blocks, as the while loop blocks and does not release the event loop.
My question is: Is it possible in any way to make this sample run as intended?
I have already experimented with setImmediate and process.nextTick and various other things, but non of them helped. Basically, what you'd need was a way to tell Node.js to please pause the current function, continue running the event loop, and getting back at a later point in time.
I know that you can achieve something similar using yield and generator functions, at least in Node.js 0.11.2 and above. But, I'm curious whether it works even without?
Please note that I am fully aware of how to do asynchronous programming in Node.js, of the event loop and all the related stuff. I am also fully aware that writing code like this is a bad idea, especially in Node.js. And I am also fully aware that an 'active wait' is a stupid idea, as well. So please don't give the advice to learn how to do it asynchronously or something like that. I know that.
The reason why I am asking is just out of curiosity and for the wish to learn.
I've recently created simpler abstraction WaitFor to call async functions in sync mode (based on Fibers). It's at an early stage but works. Please try it: https://github.com/luciotato/waitfor
using WaitFor your code will be:
console.log ( wait.for ( addAsync,23,42 ) ) ;
You can call any standard nodejs async function, as if it were a sync function.
wait.for(fn,args...) fulfills the same need as the "sync" function in your example, but inside a Fiber (without blocking node's event loop)
You can use npm fibers (C++ AddOn project) & node-sync
implement a blocking call in C(++) and provide it as a library
Yes I know-you know - BUT EVER-EVER-EVER ;)
Non-Blocking) use a control flow library
Standard propmises and yield (generator functions) will make this straigthforward:
http://blog.alexmaccaw.com/how-yield-will-transform-node
http://jlongster.com/A-Study-on-Solving-Callbacks-with-JavaScript-Generators

Categories

Resources