Creating a promise from code that uses await - node.js

I would like to write a block of code using await syntax, immediately execute it, and create a promise that waits for execution to finish. I've come up with the following way to do this.
let makePromise = async () => {
return foo && await bar();
}
let promise = makePromise();
However, I find this hard to read and understand. Creating a function and then calling it right away seems counter-intuitive and goes against good practice in other programming languages. Is there a more idiomatic way to write this piece of code?
Particularly, this code is used in the following context.
let promises = items.map((item) => {
let makePromise = async () => {
return foo(item) && await bar(item);
}
return makePromise();
});

Why not using an async function directly? The following code behaves exactly the same as your example, but keep in mind, this results in an array of promises and awaits none of them.
function foo(x) { return "foo " + x; }
async function bar(x) { return "bar " + x; }
const items = [1];
let promises = items.map(
async item => foo(item) && await bar(item)
);
Promise.all(promises).then(x => console.log(x));

Async functions actually return a Promise. This is valid:
const x = async () => {
console.log(123)
}
x().then(() => {
console.log(456)
})
So in your case:
let promise = bar()

Related

nodejs Await promises working but log shows different order [duplicate]

I have a function with multiple forEach loops:
async insertKpbDocument(jsonFile) {
jsonFile.doc.annotations.forEach((annotation) => {
annotation.entities.forEach(async (entity) => {
await this.addVertex(entity);
});
annotation.relations.forEach(async (relation) => {
await this.addRelation(relation);
});
});
return jsonFile;
}
I need to make sure that the async code in the forEach loop calling the this.addVertex function is really done before executing the next one.
But when I log variables, It seems that the this.addRelation function is called before the first loop is really over.
So I tried adding await terms before every loops like so :
await jsonFile.doc.annotations.forEach(async (annotation) => {
await annotation.entities.forEach(async (entity) => {
await this.addVertex(entity);
});
await annotation.relations.forEach(async (relation) => {
await this.addRelation(relation);
});
});
But same behavior.
Maybe it is the log function that have a latency? Any ideas?
As we've discussed, await does not pause a .forEach() loop and does not make the 2nd item of the iteration wait for the first item to be processed. So, if you're really trying to do asynchronous sequencing of items, you can't really accomplish it with a .forEach() loop.
For this type of problem, async/await works really well with a plain for loop because they do pause the execution of the actual for statement to give you sequencing of asynchronous operations which it appears is what you want. Plus, it even works with nested for loops because they are all in the same function scope:
To show you how much simpler this can be using for/of and await, it could be done like this:
async insertKpbDocument(jsonFile) {
for (let annotation of jsonFile.doc.annotations) {
for (let entity of annotation.entities) {
await this.addVertex(entity);
}
for (let relation of annotation.relations) {
await this.addRelation(relation);
}
}
return jsonFile;
}
You get to write synchronous-like code that is actually sequencing asynchronous operations.
If you are really avoiding any for loop, and your real requirement is only that all calls to addVertex() come before any calls to addRelation(), then you can do this where you use .map() instead of .forEach() and you collect an array of promises that you then use Promise.all() to wait on the whole array of promises:
insertKpbDocument(jsonFile) {
return Promise.all(jsonFile.doc.annotations.map(async annotation => {
await Promise.all(annotation.entities.map(entity => this.addVertex(entity)));
await Promise.all(annotation.relations.map(relation => this.addRelation(relation)));
})).then(() => jsonFile);
}
To fully understand how this works, this runs all addVertex() calls in parallel for one annotation, waits for them all to finish, then runs all the addRelation() calls in parallel for one annotation, then waits for them all to finish. It runs all the annotations themselves in parallel. So, this isn't very much actual sequencing except within an annotation, but you accepted an answer that has this same sequencing and said it works so I show a little simpler version of this for completeness.
If you really need to sequence each individual addVertex() call so you don't call the next one until the previous one is done and you're still not going to use a for loop, then you can use the .reduce() promise pattern put into a helper function to manually sequence asynchronous access to an array:
// helper function to sequence asynchronous iteration of an array
// fn returns a promise and is passed an array item as an argument
function sequence(array, fn) {
return array.reduce((p, item) => {
return p.then(() => {
return fn(item);
});
}, Promise.resolve());
}
insertKpbDocument(jsonFile) {
return sequence(jsonFile.doc.annotations, async (annotation) => {
await sequence(annotation.entities, entity => this.addVertex(entity));
await sequence(annotation.relations, relation => this.addRelation(relation));
}).then(() => jsonFile);
}
This will completely sequence everything. It will do this type of order:
addVertex(annotation1)
addRelation(relation1);
addVertex(annotation2)
addRelation(relation2);
....
addVertex(annotationN);
addRelation(relationN);
where it waits for each operation to finish before going onto the next one.
foreach will return void so awaiting it will not do much. You can use map to return all the promises you create now in the forEach, and use Promise.all to await all:
async insertKpbDocument(jsonFile: { doc: { annotations: Array<{ entities: Array<{}>, relations: Array<{}> }> } }) {
await Promise.all(jsonFile.doc.annotations.map(async(annotation) => {
await Promise.all(annotation.entities.map(async (entity) => {
await this.addVertex(entity);
}));
await Promise.all(annotation.relations.map(async (relation) => {
await this.addRelation(relation);
}));
}));
return jsonFile;
}
I understand you can run all the addVertex concurrently. Combining reduce with map splitted into two different set of promises you can do it. My idea:
const first = jsonFile.doc.annotations.reduce((acc, annotation) => {
acc = acc.concat(annotation.entities.map(this.addVertex));
return acc;
}, []);
await Promise.all(first);
const second = jsonFile.doc.annotations.reduce((acc, annotation) => {
acc = acc.concat(annotation.relations.map(this.addRelation));
return acc;
}, []);
await Promise.all(second);
You have more loops, but it does what you need I think
forEach executes the callback against each element in the array and does not wait for anything. Using await is basically sugar for writing promise.then() and nesting everything that follows in the then() callback. But forEach doesn't return a promise, so await arr.forEach() is meaningless. The only reason it isn't a compile error is because the async/await spec says you can await anything, and if it isn't a promise you just get its value... forEach just gives you void.
If you want something to happen in sequence you can await in a for loop:
for (let i = 0; i < jsonFile.doc.annotations.length; i++) {
const annotation = jsonFile.doc.annotations[i];
for (let j = 0; j < annotation.entities.length; j++) {
const entity = annotation.entities[j];
await this.addVertex(entity);
});
// code here executes after all vertix have been added in order
Edit: While typing this a couple other answers and comments happened... you don't want to use a for loop, you can use Promise.all but there's still maybe some confusion, so I'll leave the above explanation in case it helps.
async/await does not within forEach.
A simple solution: Replace .forEach() with for(.. of ..) instead.
Details in this similar question.
If no-iterator linting rule is enabled, you will get a linting warning/error for using for(.. of ..). There are lots of discussion/opinions on this topic.
IMHO, this is a scenario where we can suppress the warning with eslint-disable-next-line or for the method/class.
Example:
const insertKpbDocument = async (jsonFile) => {
// eslint-disable-next-line no-iterator
for (let entity of annotation.entities) {
await this.addVertex(entity)
}
// eslint-disable-next-line no-iterator
for (let relation of annotation.relations) {
await this.addRelation(relation)
}
return jsonFile
}
The code is very readable and works as expected. To get similar functionality with .forEach(), we need some promises/observables acrobatics that i think is a waste of effort.

how to get [chrome.instanceID.getID] , [chrome.storage.sync.get] by async & await?

How do you get the information for the Chrome extension by using async and await?
[chrome.instanceID.getID]
[chrome.storage.sync.get]
We tried this code:
async function test()
{
let _r = await chrome.instanceID.getID();
return _r;
}
let _pc_id = test();
but _pc_id returns a promise. We find no way to get the value in it. How should we do this?
You can get the instanceID like this, but can't store it in a variable to use it out of scope of the promise, AFAIK. You may want to read this: saving data from promise in a variable
If you want to use the returned value of Promise you need to do it in the promise or in .then()s after the promise, at least that is how I do it.
chrome.instanceID.getID example:
chrome.instanceID.getID((instance_id) => {
console.log(instance_id);
// Execute your other related code here
});
or
var promise = chrome.instanceID.getID();
promise.then((instance_id) => {
console.log(instance_id);
// Execute your other related code here
});
or
chrome.instanceID.getID()
.then((instance_id) => {
console.log(instance_id);
// Execute your other related code here
});
chrome.storage.sync.get example:
chrome.storage.sync.get('myKey', function(items) {
var key = items.myKey;
// Below code is an example, change it to your needs
if (key) {
console.log(key)
} else {
key = createKey(); // a custom function that generates keys
chrome.storage.sync.set({myKey: key}, function () {
console.log(key);
});
}
};

Ensure an async method is never executed in parallel

There's an async method in some external library, which is not thread-safe (corrupts internal state when being called concurrently) and is very unlikely to be ever fixed in the near future. Yet I want the rest of my codebase to freely use it in any possible parallel vs. sequential combinations (in other words, I want my methods that use that method to still be thread-safe).
So I need to wrap that problematic method with some Active Object implementation, so that its invocations are always aligned (executed one after another).
My take on this is to use a chain of Promises:
var workQueue: Promise<any> = Promise.resolve();
function executeSequentially<T>(action: () => Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
workQueue = workQueue.then(() => {
return action().then(resolve, reject);
});
});
}
And then instead of directly calling that method:
const myPromise = asyncThreadUnsafeMethod(123);
I'll be calling it like this:
const myPromise = executeSequentially(() => asyncThreadUnsafeMethod(123));
But am I missing something? Any potential problems with this approach? Any better ideas?
There are no problems with this approach, using a promise queue is a standard pattern that will solve your problem.
While your code does work fine, I would recommend to avoid the Promise constructor antipattern:
let workQueue: Promise<void> = Promise.resolve();
const ignore = _ => {};
function executeSequentially<T>(action: () => Promise<T>): Promise<T> {
const result = workQueue.then(action);
workQueue = result.then(ignore, ignore);
return result;
}
Also you say that you'd be "calling the method" like executeSequentially(() => asyncThreadUnsafeMethod(123));, but this does leave the potential for mistakes where you forget the executeSequentially wrapper and still call the unsafe method directly. Instead I'd recommend to introduce an indirection where you wrap the entire safe call in a function and then export only that, having your codebase only interact with that facade and never with the library itself. To help with the creation of such a facade, I'd curry the executeSequentially function:
const ignore = _ => {};
function makeSequential<T, A extends unknown[]>(fn: (...args: A) => Promise<T>) => (...args: A) => Promise<T> {
let workQueue: Promise<void> = Promise.resolve();
return (...args) => {
const result = workQueue.then(() => fn(...args));
workQueue = result.then(ignore, ignore);
return result;
};
}
export const asyncSafeMethod = makeSequential(asyncThreadUnsafeMethod);
or just directly implement it for that one case
const ignore = _ => {};
let workQueue: Promise<void> = Promise.resolve();
export function asyncSafeMethod(x: number) {
const result = workQueue.then(() => asyncThreadUnsafeMethod(x));
workQueue = result.then(ignore, ignore);
return result;
}
import { asyncSafeMethod } from …;
asyncSafeMethod(123);

How to return object after completeon of .map function in a function of type promise in nodejs

I am using nodejs for server side code. I am using .map() inside a method of type Promise. Here the loop execution executes perfectly,but the problem is that, method returns a value before the .map() function is fully executed.
Below is the code,where in i am using ,map() function. Here i want to return created_ids array only after the .map() is completely executed,but what's happening is created_ids will be returned before insert query is called.
public createDomainUser(user:any):Promise<any> {
let created_ids:any = [];
var md5 = require('md5');
(user['password']) = md5(user['password']);
return Promise.all(user.Boards.map(boardobj => {
BasePgRepo.db.query(USER_QUERIES['insert'],vals).then(rows => {
created_ids.push(rows);
})
return created_ids;
}))
}
"return created_ids" should be called after the completion of map(). Am i going wrong somewhere?
I also tried to clean your code. Your mistake was missing return before BasePgRepo call.
import md5 from "md5";
class MyClass {
async createDomainUser(user: any): Promise<any> {
let created_ids: any = [];
user['password'] = md5(user['password']);
await Promise.all(user.Boards.map((boardobj: any) =>
BasePgRepo.db.query(USER_QUERIES['insert'], vals)
.then((rows: any) => {
created_ids.push(rows);
})));
return created_ids;
}
}

Get the value of a promise and assign to variable

utility.fetchInfo() returns a Promise object. I need to be able to get the value of this Promise object and assign the value of it to a variable that I can then use later on in my code.
At the moment, I can happily print the value of result to the console, but I need to be able to assign this value to myVal. So far, I've tried a lot of things and nothing has worked.
var myVal = utility.fetchInfo().then(result => console.log(result));
Thanks
What #dhilt said but do your stuff inside the promise callback function:
utility.fetchInfo().then(result => {
doSomethingWith(result);
});
Or use async/await if you are using a recent version of Node or Babel:
async function myFunction () {
var myVal = await utility.fetchInfo();
}
Just do an assignment within the callback's body
utility.fetchInfo().then(result => { myVal = result; });
Depends on the situation, for example, if there's a big piece of async logic, it may be better to extract it in a separate function:
let myVal; // undefined until myAsyncFunc is called
const myAsyncFunc = (result) => {
console.log(result);
myVal = result;
// ...
};
utility.fetchInfo().then(myAsyncFunc); // async call of myAsyncFunc
When you return the value to another file/function and you are getting undefined because another function where you are using that value is not waiting for the value to be assigned to the variable. For example there are two function
function1(){
var x=desired_value
}
function2(){
var y = x
}
now function1 might take some time to assign desire value to var x and java script ll not wait for this, it ll start running function2 and when you read the value of y you will get undefined.
To solve this problem we can use java script promise/await/async, return value from async function as resolve. refer below example
async function1(){ //function1 should be async here for this to work
return new Promise(function(resolve,reject){
resolve(desired_value)
});
}
or above function can be writtern as
async function1(){ //function1 should be async here for this to work
return Promise.resolve(desired_value)
}
Now we can use this value in another function by calling the function1 and use await keyword
function2(){
var y = await function1();
}
now it ll wait for function1 to complete before assigning its value to y.
I think what you are looking for is some fancy ES7 syntax:
var myVal = (async () => {
var data = await utility.fetchInfo();
return data;
})();
(async () => {
console.log(await myVal);
})();
Keep in mind console.log(myVal) will end up with Promise { <pending> }, So instead you would use
(async () => {
console.log(await myVal);
})();
which would return your desired output.

Resources