Promises and async functions "borrowing" variables from concurrently running promises - javascript

I have this Waterline async call inside helper function answerUserTag:
theQuestion = await UserTag.findOne({id: answerObject.tag});
This is how I call the above helper:
const promises = userTagAnswers.map(userTagAnswer =>
sails.helpers.answerUserTag.with(
{
userTagAnswer: userTagAnswer,
answeringUserId: inputs.userId,
userType: inputs.userType
}));
await Promise.all(promises);
If there's just one userTagAnswers (i.e. one promise), it works fine. But if there's two userTagAnswers and I set a breakpoint after the theQuestion query in the helper, I see the following non-sensible values:
answerObject.tag is equaled to 5c338ae267a1983e84340388
theQuestion.id is equaled to 5c27227ac1e60913703f3002
It seems like there's an illogical overlap between variables when multiple promises are called.

Try to debug it through Promise.each() perhaps? Since the iteration is sequential, you'll know which Promise is troubling you.
Secondly, it would be great if you specify the helper function.
Thirdly, why with? If you check MDN - with, it clearly states and I quote:
Use of the with statement is not recommended, as it may be the source of confusing bugs and compatibility issues. It has Ambiguity contra. The with statement makes it hard for a human reader or JavaScript compiler to decide whether an unqualified name will be found along the scope chain, and if so, in which object.

The inconsistant results are because the promises are resolving async
this may help: toniov.github.io/p-iteration

Related

How does this promise without an argument work in this mutation observer?

I was looking at this mutation obersver in some typescript code and I can’t figure how the promise works in it, I’ve not see a promise without an argument in this style before:
const observer = new MutationObserver((mutations: MutationRecord[]) => {
Promise.resolve().then(someNextMethod)
this.somethingThatTakesAWhile()
})
observer.observe(HTMLtarget)
When the observation is triggered the someNextMethod runs after this.somethingThatTakesAWhile() has ran. How is this possible in this instance? I don't understand how the Promise get passed any arguments and knows how to resolve in this case. Could someone explain the internal mechanics of this code please? I'm at a bit of a loss as to how it runs in that order. Thanks!
The main point of something like this:
Promise.resolve().then(someNextMethod)
is just to call someNextMethod() after the current chain of execution finishes. In a browser, it is similar to this:
setTimeout(someNextMethod, 0);
though the Promise.resolve() method will prioritize it sooner than setTimeout() if other things are waiting in the event queue.
So, in your particular example, the point of these two statements:
Promise.resolve().then(someNextMethod)
this.somethingThatTakesAWhile()
is to call someNextMethod() after this.somethingThatTakesAWhile() returns and after the MutationObserver callback has returned, allowing any other observers to also be notified before someNextMethod() is called.
As to why this calls someNextMethod() later, that's because all .then() handlers run no sooner than when the current thread of execution completes (and returns control back to the event loop), even if the promise they are attached to is already resolved. That's how .then() works, per the Promise specification.
Why exactly someone would do that is context dependent and since this is all just pseudo-code, you don't offer any clues as to the real motivation here.
This:
Promise.resolve().then(someNextMethod)
Is equivalent to this:
Promise.resolve().then(() => someNextMethod())
Working backward is equivalent to:
const myNewMethod = () => someNextMethod()
Promise.resolve().then(myNewMethod)
Defining a function inline or pointing to a function is just a syntactical difference. All of the arguments passed through then will be passed to the referenced function. But in this case, there isn't any as it's a promise with an empty return value.
In other words, the method doesn't need any parameters. In this instance it's actually just an old JS hack/trick to get the code to run at the end of the call stack.

Unawaited promises still executed

While debugging a set-up to control promise concurrency for some CPU intensive asynchronous tasks, I came across the following behavior that I can not understand.
My route to control the flow was to first create an array of Promises, and after that control the execution of these Promises by explicitly awaiting them. I produced the following snippet that shows the unexpected behavior:
import fs from 'node:fs';
import { setTimeout } from 'node:timers/promises';
async function innerAsync(n: number) {
return fs.promises.readdir('.').then(value => {
console.log(`Executed ${n}:`, value);
return value;
});
}
async function controllerAsync() {
const deferredPromises = new Array<Promise<string[]>>();
for (const n of [1, 2, 3]) {
const defer = innerAsync(n);
deferredPromises.push(defer);
}
const result = deferredPromises[0];
return result;
}
const result = await controllerAsync();
console.log('Resolved:', result);
await setTimeout(1000);
This snippet produces the following result (assuming 2 files are in .):
Executed 1: [ 'test.txt', 'test2.txt' ]
Resolved: [ 'test.txt', 'test2.txt' ]
Executed 2: [ 'test.txt', 'test2.txt' ]
Executed 3: [ 'test.txt', 'test2.txt' ]
Observed
The promises defined at line 15 are all being executed, regardless if they are returned/awaited (2 and 3).
Question
What causes the other, seemingly unused, promises to be executed? How can I make sure in this snippet only the first Promise is executed, where the others stay pending?
Node v19.2
Typescript v4.9.4
StackBlitz
What causes the other, seemingly unused, promises to be executed?
A promise is a tool used to watch something and provide an API to react to the something being complete.
Your innerAsync function calls fs.promises.readdir. Calling fs.promises.readdir makes something happen (reading a directory).
The then callback is called as a reaction when reading that directory is complete.
(It isn't clear why you marked innerAsync as async and then didn't use await inside it.)
If you don't use then or await, then that doesn't change the fact you have called fs.promises.readdir!
How can I make sure in this snippet only the first Promise is executed, where the others stay pending?
Focus on the function which does the thing, not the code which handles it being complete.
Or an analogy:
If you tell Alice to go to the shops and get milk, but don't tell Bob to put the milk in the fridge when Alice gets back, then you shouldn't be surprised that Alice has gone to the shops and come back with milk.
Promises aren't "executed" at all. A promise is a way to observe an asynchronous process that is already in progress. By the time you have a promise, the process it's reporting the result of is already underway.¹ It's the call to fs.readdir that starts the process, not calling then on a promise. (And even if it were, you are calling then on the promise from fs.readdir right away, in your innerAsync function.)
If you want to wait to start the operations, wait to call the methods starting the operations and giving you the promises. I'd show an edited version of your code, but it's not clear to me what it's supposed to do, particularly since controllerAsync only looks at the first element of the array and doesn't wait for anything to complete before returning.
A couple of other notes:
It's almost never useful to combine async/await with explicit calls to .then as in innerAsync. That function would be more clearly written as:
async function innerAsync(n: number) {
const value = await fs.promises.readdir(".");
console.log(`Executed ${n}:`, value);
return value;
}
From an order-of-operations perspective, that does exactly the same thing as the version with the explicit then.
There's no purpose served by declaring controllerAsync as an async function, since it never uses await. The only reason for making a function async is so you can use await within it.
¹ This is true in the normal case (the vast majority of cases), including the case of the promises you get from Node.js' fs/promises methods. There are (or were), unfortunately, a couple of libraries that had functions that returned promises but didn't start the actual work until the promise's then method was called. But those were niche outliers and arguably violate the semantics of promises.

Does Typescript Async method return a promise?

I am just getting up to speed on Async/Await in Typescript.
I am converting existing code like this:
getImportanceTypes(): Promise<void> {
return this.importanceTypeService.list()
.then(items => {
this.importanceTypes = items;
});
}
to:
async getImportanceTypes(): Promise<void> {
this.importanceTypes = await this.importanceTypeService.list()
}
The question is: Does this really return a promise? It must, because it compiles successfully, but in my mind, I see the code execution suspending on the await until it completes, then continuing.
The reason I ask is that I have about 10 similiar calls to the above (different type tables) and I would like them to execute in parallel with Promise.all.
Yes, async functions return promises. (In JavaScript and TypeScript both.) async/await is "just" syntactic sugar for the creation and consumption of promises (but, you know, really useful sugar).
The way you've declared that return type is indeed the correct way to do it. There's been some dissention on that point, however. :-) There are some who feel that if the function is declared async, you should be able to just specify its resolution type rather than explicitly mentioning the promise. At present, though, you do use Promise<x> rather than just x.

Async functions vs await

I'm coming from a PHP background and I'm trying to learn NodeJS.
I know that everything in Node is async but I've found that i've been using the async / await combo quite a lot in my code and I wanted to make sure I wasn't doing something stupid.
The reason for it is that I have a lot of situations where I need the result of something before continuing (for example for a small ajax request). This small ajax request has no other purpose other than to do the set of things that I want it to and in the order that I specify so even if I do things the "async way" and write it using a callback i'm still having to wait for things to finish in the right order.
Right now whenever I find myself in this situation I just use await to wait for the result:
ie:
var result = await this.doSomething(data);
Opposed to using a callback
this.doSomething(data, function(callback) {
// code
callback();
});
To me the first example looks cleaner than the second one which is why I've been opting for that. But I'm worried that I might be missing something fundamental here. But in a situation where there is nothing else to process below the async call and the only way for things to progress is for it to follow a syncronous style, is there anything wrong with using the first style over the second?
But I'm worried that I might be missing something fundamental here.
Nope, you're not, that's exactly what you want to do, assuming this.doSomething(data) is asynchronous (and if it's an ajax call, one hopes it is async) and that it returns a promise (which all functions defined with the async keyword do implicitly). (If it's not asynchronous, you don't need the await, although it's allowed.)
You're probably being a bit confused (understandably) by the fact that things are going through a transition. Until recently, the overwhelming convention in Node APIs (the built-in ones and ones provided by third-party modules) was to use the "Node callback" pattern, which is that a function that will do asynchronous work expects its last argument to be a callback, which it will call with a first argument indicating success/failure (null = success, anything else is an error object) with subsequent arguments providing the result. (Your second example assumes doSomething is one of these, instead of being a function that returns a promise.)
Example: fs.readFile, which you use like this:
fs.readFile("/some/file", "utf-8", function(err, data) {
if (err) {
// ...handle the fact an error occurred..
return;
}
// ...use the data...
});
This style quickly leads to callback hell, though, which is one of the reasons promises (aka "futures") were invented.
But a lot of Node APIs still use the old pattern.
If you need to use "Node callback"-pattern functions, you can use util.promisify to create promise-enabled versions of them. For instance, say you need to use fs.readFile, which uses the Node callback pattern. You can get a promise version like this:
const readFilePromise = util.promisify(fs.readFile);
...and then use it with async/await syntax (or use the promise directly via then):
const data = await readFilePromise("/some/file", "utf-8");
There's also an npm module called promisify that can provide a promise-ified version of an entire API. (There's probably more than one.)
Just because promises and async/await replace the old Node callback style in most cases doesn't mean callbacks don't still have a place: A Promise can only be settled once. They're for one-off things. So callbacks still have a place, such as with the on method of EventEmitters, like a readable stream:
fs.createReadStream("/some/file", "utf-8")
.on("data", chunk => {
// ...do something with the chunk of data...
})
.on("end", () => {
// ...do something with the fact the end of the stream was reached...
});
Since data will fire multiple times, it makes sense to use a callback for it; a promise wouldn't apply.
Also note that you can only use await in an async function. Consequently, you may find yourself getting into the habit of a "main" module structure that looks something like this:
// ...Setup (`require` calls, `import` once it's supported, etc.)...
(async () => {
// Code that can use `await `here...
})().catch(err => {
// Handle the fact an error/promise rejection occurred in the top level of your code
});
You can leave the catch off if you want your script to terminate on an unhandled error/rejection. (Node doesn't do that yet, but it will once unhandled rejection detection matures.)
Alternately, if you want to use a promise-enabled function in a non-async function, just use then and catch:
this.doSomething()
.then(result => {
// Use result
})
.catch(err => {
// Handle error
});
Note: async/await is directly supported in Node 7.x and above. Be sure your target production environment supports Node 7.x or above if your'e going to use async/await. If not, you could transpile your code using something like Babel and then use the transpiled result on the older version of Node.

removing promise dependencies

I'm aware of the power of promises, however I have several old functions that are synchronous:
function getSomething() {
return someExternalLibrary.functionReturnsAValue()
}
console.log(getSomething()); // eg prints 'foo'
Unfortunately, when someExternalLibrary updated, it has removed functionReturnsAValue() and has lumped me with functionReturnsAPromise():
function getSomething() {
return someExternalLibrary.functionReturnsAPromise()
}
console.log(getSomething()); // now prints '[object]'
This of course, breaks absolutely everything written that depends on what used to be a simple value.
Obviously, I'd prefer two things:
ask the original library to keep a synchronous return value. (Not going to happen -- b/c they have refused)
A way to actually wait for a value
I have read numerous articles on why promises are great, ad nauseam, but the simple fact is: If I embrace promises, all I really do is shuffle promises onto some other part of the code, which then must deal with the promise of a value...
Is there a way (in nodejs) to actually wait for a promise to get itself together?
The best I can find is to use coroutines and yield, but really, it's still passing the buck. To be clear, I want the function getSomething to continue to return a value. Is there a way to do it?
Clearly, I fear I've misunderstood something about Promises...
The app is for non-browser implementations and runs purely from the command line. I've been trying to understand how bluebird's reflect() might help, to no avail.
(Yes, I'm aware this question has been asked many times in various formats, but I can't find a suitable answer to the core issue. If anything, I'm looking for the opposite of this question. The closest related (but unhelpful) question I can find is: Managing promise dependencies.)
There's the concept of generator functions. These are a special kind of function in both syntax (asterisk notation) and semantics. Unlike regular functions, generator functions return something that's also new to ECMAScript: iterators. Iterators happen to be objects made specifically to be iterated on, e.g. with the all new for...of loop. They can be also iterated on manually by calling their 'next' method. Each such call produces an object containing two properties: 'value' (iterator's current value) and 'done' (a boolean indicating whether we reached the last value of the iterable). However, the best thing about generator functions is their ability to suspend their execution each time a keyword 'yield' is encountered. Let's have a glimpse of how it all works together:
'use strict';
let asyncTask = () =>
new Promise((resolve, reject) => {
if (Math.random() > 0.5) {
resolve(1);
} else {
reject(new Error('Something went wrong'));
}
});
let makeMeLookSync = fn => {
let iterator = fn();
let loop = result => {
!result.done && result.value.then(
res => loop(iterator.next(res)),
err => loop(iterator.throw(err))
);
};
loop(iterator.next());
};
makeMeLookSync(function* () {
try {
let result = yield asyncTask();
console.log(result);
} catch (err) {
console.log(err.message);
}
});
The short answer
I am told repeatedly: You can't undo functions that have been promisified.
Edit: An upcoming solution
It appears that the ES2017 (although still draft), goes a long way in making promisified code easier to work with:
https://ponyfoo.com/articles/understanding-javascript-async-await
It seems that there is also a node library ready for this support too: https://github.com/normalize/mz.
Using this methodology, having apis converted to Promises won't be so bad (although it still appears that promises still poison the rest of the codebase):
const fs = require('mz/fs')
async function doSomething () {
if (await fs.exists(__filename)) // do something
}
The rest of this answer is just a general commentary on the problem.
Why we need a solution
Let's start with a sample piece of traditional synchronous code, in 3 flavours from more 'older-fashioned' to 'newer':
This is the traditional javascript way, requiring exception based programming to handle unexpected errors:
function getSomething() {
if (someproblem) throw new Error('There is a problem');
return 'foo';
}
However, adding try/ catch statements becomes very laborious and tedious, very quickly.
With the advent of node.js, callbacks were made popular, which nicely circumvented the issue, since each caller was explicitly forced to deal with error conditions in the same callback. This meant less errors in the caller's code:
function getSomething(callback) {
if (callback) {
if (someproblem)
callback(new Error('There is a problem'), null);
else
callback(null, 'foo');
}
return 'foo';
}
Then, the after some teething issues, node.js quickly proved itself for server-side communications, and people were amazed at the speed that asynchronous solutions provided. Node application frameworks like Express and Meteor grew, which focused on this.
Unfortunately, using the same callback scheme quickly became troublesome and the developers dealing in asynchronous code started using Promises in an effort to linearize the code, to make it readable, like the traditional (try/catch) code was.
The problem is that it got evangenlized too much. Everyone started thinking that Promises are the way to go. Personally, I call it a poison on a codebase. Once you have anything that uses Promises, your whole codebase must become asynchronous. This is not always a sensible nor a practical solution, IMHO.
The worst of all side effects is that the above function, even though it is completely synchronous, can be written in Promises too:
var bluebird = require('bluebird');
function getSomething() {
// IMHO, this is ridiculous code, but is increasingly popular.
if (someproblem) return Promise.reject(new Error('There is a problem'));
return Promise.resolve('foo');
}
For those who doubt this is a problem, perhaps should look at the SO question: How do I convert an existing callback API to promises?. Pay particular attention to #3, Node-style callback.
So, for anyone who cares, I would like to suggest that there needs to be a 'pill' for Promises. I urge that we need more than promises: we need results, and sometimes in a timely manner.
Take a look at the default node.js api. It does not use Promises. It also provides both synchronous and asynchronous calls to appropriate parts of the api (eg File System).
For those of you who feel tempted to downvote this answer: that is your prerogative, but there are clear issues on when Promises are not the answer, and I feel strongly that there are cases when we need to be able to re-synchronize decoupled code.
I also apologize for this 'blog-post' styled answer.

Categories

Resources