Cannot resolve Promise in Node.js App with chrome-cookies-secure module - javascript

I'm working on a local Node.js app that needs to access the Google Chrome cookies. I've found the chrome-cookies-secure library that seems to do the job but I just can't figure out what's wrong with the code below.
const chrome = require('chrome-cookies-secure');
const domains = [
"google.com"
];
const resolveCookies = async () => {
let result = [];
for(domain of domains) {
await chrome.getCookies(`https://${domain}/`, (err, cookies) => {
result.push(cookies);
// console.log(cookies); //? This is going to correctly print the results
})
}
return result;
}
const final = resolveCookies();
console.log(final); //! This is going to return a Promise { <pending> } object
The idea is that I just want to store the cookies from all the domains in a list but no matter what I cannot resolve the Promise.
I didn't see any examples with the async call for this module but if I don't use it it's going to return me an empty list after the script execution.
My Node Version: v14.4.0
What am I doing wrong?

It looks like the implementation of getCookies is not correctly awaiting the asynchronous processes. You can see in the implementation that although getCookies itself is async, it calls getDerivedKey without awaiting it (and that function isn't async anyway).
Rather than trying to rely on this implementation, I'd suggest using Util.promisify to create a proper promise via the callback:
const util = require('util');
const chrome = require('chrome-cookies-secure');
const getCookies = util.promisify(chrome.getCookies);
// ...
const cookies = await getCookies(`https://${domain}/`);
Note that, as Reece Daniels pointed out in the comments, the getCookies implementation actually takes a profile parameter after the callback; if you need to use that parameter, you can't use the built-in promisify. You'd have to wrap it yourself instead, this could look like e.g.:
const getCookies = (url, format, profile) => new Promise((resolve, reject) => {
chrome.getCookies(url, format, (err, cookies) => {
if (err) {
reject(err);
} else {
resolve(cookies);
}
}, profile);
});
They already tried to fix the promise upstream, but the PR hasn't been merged in nearly nine months.
Note that once you have a working function to call you can also convert:
const resolveCookies = async () => {
let result = [];
for(domain of domains) {
await chrome.getCookies(`https://${domain}/`, (err, cookies) => {
result.push(cookies);
// console.log(cookies); //? This is going to correctly print the results
})
}
return result;
}
to simply:
const resolveCookies = () => Promise.all(domains.map((domain) => getCookies(`https://${domain}/`)));

An async function returns a Promise.
So your resolveCookies function will also return a Promise as you noticed.
You need to either chain the console.log with a .then e.g.
resolveCookies().then(console.log);
Or if you need to set it to a variable like final you need to await that Promise too. In that case you need an async IIFE:
(async () => {
const final = await resolveCookies();
console.log(final);
})();

try this.
const chrome = require('chrome-cookies-secure');
const domains = [
"www.google.com"
];
const resolveCookies = async() => {
let result = [];
for (domain of domains) {
const cookies = await getCookies(domain)
result.push(cookies)
}
return Promise.resolve(result);
}
const getCookies = async (domain) => {
chrome.getCookies(`https://${domain}/`, (err, cookies) => {
return Promise.resolve(cookies);
})
}
resolveCookies().then((resp) => {
console.log('FINAL ',resp)
}).catch((e) => {
console.log('ERROR ', e)
})

Related

JavaScript Dynamic Promises

I am trying to understand how promises work in JS by playing with swapi.dev. I would like to create a dynamic chain of promises (not using async/await) but it does not provide me with any result. In particular, the idea behind is to get all names of the given person (for instance Luke Skywalker) and dump them into the console.
Could anyone help me? What am I missing?
Thanks in advance.
"use strict";
const request = require("request-promise");
const BASE_URL = "http://swapi.dev/api";
var currentPromise = Promise.resolve();
callApiPromise(`${BASE_URL}/people/1`).then((data) => {
console.log("Getting vehicles' URLs");
const vehicles_URL = data["vehicles"];
console.log("Starting looping through URLs");
for (let i = 0; i < vehicles_URL.length; i++) {
console.log(`i=${i}, vehicle_URL=${vehicles_URL[i]}`);
currentPromise = currentPromise.then(function () {
console.log(".. getting vehicle name");
return getVehicleName[vehicles_URL[i]];
});
}
});
function getVehicleName(url) {
callApiPromise(url).then((vehicle_data) => {
var arrVehicleData = new Array();
arrVehicleData.push(vehicle_data);
console.log(arrVehicleData.map((vehicle) => vehicle.name));
});
}
function callApiPromise(url) {
return new Promise((resolve, reject) => {
callApi(url, (err, data) => {
if (err) {
reject(err);
return;
}
resolve(data);
});
});
}
function callApi(url, callback) {
request
.get(url)
.then((response) => {
const json = JSON.parse(response);
callback(null, json);
})
.catch((err) => {
callback(err, null);
});
}
Some issues:
A missing return statement in getVehicleName
A syntax issue in getVehicleName[vehicles_URL[i]] (should be parentheses)
As the promises for getting the vehicle names are independent, you would not chain them, but use Promise.all
arrVehicleData will always only have one element. There is no reason for an array there where it is used.
You are also taking the wrong approach in using request.get. The bottom function turns that API from a Promise-API to a callback API, only to do the reverse (from callback to promise) in the function just above it. You should just skip the callback layer and stick to promises:
"use strict";
const request = require("request-promise");
const BASE_URL = "http://swapi.dev/api";
getJson(`${BASE_URL}/people/1`).then(data => {
return Promise.all(data.vehicles.map(getVehicleName));
}).then(vehicleNames => {
console.log(vehicleNames);
// Continue here...
});
function getVehicleName(url) {
return getJson(url).then(vehicle => vehicle.name);
}
function getJson(url, callback) {
return request.get(url).then(JSON.parse);
}
Finally, you should not use request-promise anymore since the request module, on which request-promise depends, has been deprecated
The getVehicleName doesn't return a promise. Instead it invokes a promise that by the time it will be resolved, the for loop invoking it will already be removed from the call stack.
This is a sample of promise chaining:
const promise = new Promise(resolve => resolve(1))
const promise1 = Promise.resolve(2)
const methodReturnPromise = () => new Promise(resolve => resolve(3))
promise.then(firstPromiseData => {
// do something with firstPromiseData
console.log(firstPromiseData)
return promise1
}).then(secondPromiseData => {
// do something with secondPromiseData
console.log(secondPromiseData)
return methodReturnPromise()
}).then(thirdPromiseData => {
// do something with thirdPromiseData
console.log(thirdPromiseData)
})

How to use async/await using crypto.randomBytes in nodejs?

const crypto = require('crypto');
async function getKey(byteSize) {
let key = await crypto.randomBytes(byteSize);
return key;
}
async function g() {
let key = await getKey(12);
return key;
}
console.log(g());
console.log('hello - want this called after g() above');
I've been at this for an hour and I can't understand how to ensure that I get a key using async/await. I get a Promise-pending no matter what I do.
I've also tried this:
async function getKey(byteSize) {
let key = await crypto.randomBytes(byteSize);
return key;
}
getKey(12).then((result) => { console.log(result) })
console.log('hello');
... to no avail! Which was inspired by:
How to use await with promisify for crypto.randomBytes?
Can anyone help me with this?
All I'm trying to do is to get randomBytes async. using the async./await block but ensure that it fulfills the promise before I carry on in the code.
This is an extension of my comment on the question
Since you're not promisify'ing or passing a callback to crypto.randomBytes() it is synchronous so you can't await it. Additionally, you're not properly awaiting the promise returned by g() at the top level. That is why you always see the pending Promise in your console.log()
You can use util.promisify() to convert crypto.randomBytes() into a promise returning function and await that. There is no need for the async/await in your example because all that is doing is wrapping a promise with a promise.
const { promisify } = require('util')
const randomBytesAsync = promisify(require('crypto').randomBytes)
function getKey (size) {
return randomBytesAsync(size)
}
// This will print the Buffer returned from crypto.randomBytes()
getKey(16)
.then(key => console.log(key))
If you want to use getKey() within an async/await style function it would be used like so
async function doSomethingWithKey () {
let result
const key = await getKey(16)
// do something with key
return result
}
If the callback function is not provided, the random bytes are generated synchronously and returned as a Buffer
// Synchronous
const {
randomBytes
} = await import('node:crypto');
const buf = randomBytes(256);
console.log(
`${buf.length} bytes of random data: ${buf.toString('hex')}`);
const crypto = require('crypto');
async function getKey(byteSize) {
const buffer = await new Promise((resolve, reject) => {
crypto.randomBytes(byteSize, function(ex, buffer) {
if (ex) {
reject("error generating token");
}
resolve(buffer);
});
}
async function g() {
let key = await getKey(12);
return key;
}
const crypto = require("crypto");
async function getRandomBytes(byteSize) {
return await new Promise((resolve, reject) => {
crypto.randomBytes(byteSize, (err, buffer) => {
if (err) {
reject(-1);
}
resolve(buffer);
});
});
}
async function doSomethingWithRandomBytes(byteSize) {
if (!byteSize) return -1;
const key = await getRandomBytes(byteSize);
//do something with key
}
doSomethingWithRandomBytes(16);

How do I make a long list of http calls in serial?

I'm trying to only make one http call at time but when I log the response from getUrl they are piling up and I start to get 409s (Too many requests)
function getUrl(url, i, cb) {
const fetchUrl = `https://api.scraperapi.com?api_key=xxx&url=${url.url}`;
fetch(fetchUrl).then(async res => {
console.log(fetchUrl, 'fetched!');
if (!res.ok) {
const err = await res.text();
throw err.message || res.statusText;
}
url.data = await res.text();
cb(url);
});
}
let requests = urls.map((url, i) => {
return new Promise(resolve => {
getUrl(url, i, resolve);
});
});
const all = await requests.reduce((promiseChain, currentTask) => {
return promiseChain.then(chainResults =>
currentTask.then(currentResult => [...chainResults, currentResult]),
);
}, Promise.resolve([]));
Basically I don't want the next http to start until the previous one has finished. Otherwise I hammer their server.
BONUS POINTS: Make this work with 5 at a time in parallel.
Since you're using await, it would be a lot easier to use that everywhere instead of using confusing .thens with reduce. It'd also be good to avoid the explicit Promise construction antipattern. This should do what you want:
const results = [];
for (const url of urls) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(response); // or whatever logic you need with errors
}
results.push(await response.text());
}
Then your results variable will contain an array of response texts (or an error will have been thrown, and the code won't reach the bottom).
The syntax for an async function is an async keyword before the argument list, just like you're doing in your original code:
const fn = async () => {
const results = [];
for (const url of urls) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(response); // or whatever logic you need with errors
}
results.push(await response.text());
}
// do something with results
};
To have a limited number of requests at a time, make a queue system - when a request completes, recursively call a function that makes another request, something like:
const results = [];
const queueNext = async () => {
if (!urls.length) return;
const url = urls.shift();
const response = await fetch(url);
if (!response.ok) {
throw new Error(response); // or whatever logic you need with errors
}
results.push(await response.text());
await queueNext();
}
await Promise.all(Array.from({ length: 5 }, queueNext));
// do something with results
You cannot use Array methods to sequentually run async operations because array methods are all synchronous.
The easiest way to achieve sequential async tasks is through a loop. Otherwise, you will need to write a custom function to imitate a loop and run .then after a async task ends, which is quite troublesome and unnecessary.
Also, fetch is already returning a Promise, so you don't have to create a Promise yourself to contain that promise returned by fetch.
The code below is a working example, with small changes to your original code (see comments).
// Fake urls for example purpose
const urls = [{ url: 'abc' }, { url: 'def', }, { url: 'ghi' }];
// To imitate actual fetching
const fetch = (url) => new Promise(resolve => {
setTimeout(() => {
resolve({
ok: true,
text: () => new Promise(res => setTimeout(() => res(url), 500))
});
}, 1000);
});
function getUrl(url, i, cb) {
const fetchUrl = `https://api.scraperapi.com?api_key=xxx&url=${url.url}`;
return fetch(fetchUrl).then(async res => { // <-- changes here
console.log(fetchUrl, 'fetched!');
if (!res.ok) {
const err = await res.text();
throw err.message || res.statusText;
}
url.data = await res.text();
return url; // <--- changes here
});
}
async function getAllUrls(urls){
const result = [];
for (const url of urls){
const response = await getUrl(url);
result.push(response);
}
return result;
}
getAllUrls(urls)
.then(console.log);
async/await is perfect for this.
Assuming you have an array of URLs as strings:
let urls = ["https://example.org/", "https://google.com/", "https://stackoverflow.com/"];
You simply need to do:
for (let u of urls) {
await fetch(u).then(res => {
// Handle response
}).catch(e => {
// Handle error
});
}
The loop will not iterate until the current fetch() has resolved, which will serialise things.
The reason array.map doesn't work is as follows:
async function doFetch(url) {
return await fetch(url).then(res => {
// Handle response
}).catch(e => {
// Handle error
});
}
let mapped = urls.map(doFetch);
is equivalent to:
let mapped;
for (u of urls) {
mapped.push(doFetch(u));
}
This will populate mapped with a bunch of Promises immediately, which is not what you want. The following is what you want:
let mapped;
for (u of urls) {
mapped.push(await doFetch(u));
}
But this is not what array.map() does. Therefore using an explicit for loop is necessary.
Many people provided answers using for loop. But in some situation await in for loop is not welcome, for example, if you are using Airbnb style guide.
Here is a solution using recursion.
// Fake urls for example purpose
const urls = [{ url: 'abc' }, { url: 'def', }, { url: 'ghi' }];
async function serialFetch(urls) {
return await doSerialRecursion(
async (url) => {
return result = await fetch(url)
.then((response) => {
// handle response
})
.catch((err) => {
// handle error
});
},
urls,
0
);
}
async function doSerialRecursion(fn, array, startIndex) {
if (!array[startIndex]) return [];
const currResult = await fn(array[startIndex]);
return [currResult, ...(await doSerialRecursion(array, fn, startIndex + 1))];
}
const yourResult = await serialFetch(urls);
The doSerialRecursion function will serially execute the function you passed in, which is fetch(url) in this example.

When could async/await in javascript be useful?

I understand that the following code, will output resolved. My question is how is this useful, and when could async/await be useful when building real world application in react, node, etc ?
function foo() {
const resolveAfter2Seconds = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}
async function asyncCall(){
console.log('calling');
var result = await resolveAfter2Seconds();
console.log(result);
}
return asyncCall;
}
const myFoo = foo()
myFoo();
The Network, Files, and Frequent Promises
I believe that the most common issues you'll run into that will make you want to switch your current function to async mode usually have to do with: network requests, operating on files, and the frequent use and/or nesting of promises.
Network Requests
When I write network requests I always use the async/await await combo. For me, it makes my code much more linear and readable. I also don't have to worry about returning the promise from fetch (or axios) when I'm done.
async function getPins(user, filterValue) {
const pins = await axios.get(...);
if (filterValue) {
return pins.filter(pin => pin.title.includes(filterValue));
}
return pins;
}
Files
async function handleFile(data, isEncrypted, isGzipped) {
if (isEncrypted) {
data = await decrypt(data, secret);
}
if (isGzipped) {
data = await ungzip(data);
}
return data;
}
Frequent Promises
async function init() {
const activeBoard = await getActiveBoard();
const boardMembers = await getBoardMembersFrom(activeBoard);
const allTasks = [];
for await (const tasks of boardMembers.map(getTasks)) {
allTasks.push(task);
this.setState({ tasks: [...allTasks] });
}
}
Note: you can use async/await with Promises. There's no need to limit yourself to one or the other.
const test = async props => {
const data = await fetch('...').then(res => res.json());
console.log(data) /* do what you want with the data now */
}

Returning a promise when using async/await in Firebase Cloud Functions

So I've happily been using async/await since node 8 is supported on Firebase Cloud Functions. I am struggling with 1 thing though. When using callable functions, it is told that you have to return a promise in the function, otherwise it won't work correctly. When using raw promises, its clear to me how to use it:
exports.createBankAccount = functions.region('europe-west1').https.onCall((data, context) => {
return promiseMethod().then((result) => {
return nextPromise(result);
}).then((result) => {
return result;
}).catch((err) => {
// handle err
})
});
But now, with async await, I'm not sure how to return this "chain of promises":
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
return res2;
// ??? Where to return the promise?
});
Does somebody know?
HTTP functions don't return a promise. They just send a result. You still have to use promises correctly in order to send the result, but a return value is not required. HTTP functions are terminated when the response is sent. See the documentation for more details:
Terminate HTTP functions with res.redirect(), res.send(), or res.end().
"await" is just syntax sugar for returning a Promise
When you write an async function, the code will actually exit the function and return a Promise at the first await it encounters. All code after the await will be converted to a then().
So for firebase writing code with async/await is perfectly save and in my experience even less error-prone, since I can more easily structure try&catch in my code!
Proof:
Just run this in your console:
async function iAmAsync() {
await new Promise(r => window.setTimeout(r, 1000))
return 'result'
}
let x = iAmAsync()
console.log(x)
Will print: Promise{<resolved>: "result"}
TL;DR: You don't need to change anything - if you write code with multiple awaits, this will be handled by firebase like a chain of promises and everything will just work.
And since my answer was downvoted, here is an authorative code-sample by the google firebase team itself:
https://github.com/firebase/functions-samples/blob/master/quickstarts/uppercase/functions/index.js
exports.addMessage = functions.https.onRequest(async (req, res) => {
// [END addMessageTrigger]
// Grab the text parameter.
const original = req.query.text;
// [START adminSdkPush]
// Push the new message into the Realtime Database using the Firebase Admin SDK.
const snapshot = await admin.database().ref('/messages').push({original: original});
// Redirect with 303 SEE OTHER to the URL of the pushed object in the Firebase console.
res.redirect(303, snapshot.ref.toString());
// [END adminSdkPush]
});
You nailed it with your example code.
Async/await is just a newer way of promise. They can be used interchangeable.
Here is an example promise and async/await of the same function.
This
exports.createBankAccount = functions.region('europe-west1').https.onCall((data, context) => {
return promiseMethod().then((result) => {
return nextPromise(result);
}).catch((err) => {
// handle error here
})
});
is equivalent to this:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
try {
const result = await promiseMethod();
return nextPromise(result); // You are returning a promise here
}catch(e) {
// handle error here
}
});
Note that in both cases, you are returning a promise at the end. The return value of this onCall function would be whatever nextPromise(result) is. Since you are returning nextPromsie(result), you don't need to await it.
To see the code solution to your question look at the answer of dshukertjr.
If you want to understand how to return a "chain of promises" with async/await, here is your answer:
You cant !
Why ? Because await is used to wait for a Promise to complete. Once await return a value their is no more Promise.
So if you absolutely want to return a promise using await, you can wait for one of the two functions that return promises but not both.
Here is two way to do that:
A :
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
try {
const result = await promiseMethod();
return nextPromise(result); // You are returning a promise here
}catch(e) {
// handle error here
}
});
B:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
return promiseMethod().then(async (result) => {
return await nextPromise(result);
}).catch((err) => {
// handle err
})
});
The only difference between A and B is that A waits for "PromiseMethod" to complete before returning a Promise. Whereas B returns a Promise right after being called.
Seems, we have to wait for several Promises in way like this:
const listOfAsyncJobs = [];
listOfAsyncJobs.push(createThumbnail(1, ...));
listOfAsyncJobs.push(createThumbnail(2, ...));
listOfAsyncJobs.push(createThumbnail(3, ...));
...
return Promise.all(listOfAsyncJobs); // This will ensure we wait for the end of the three aync tasks above.
From async method whatever you return it gets wrapped in promise automatically.
e.g
const myFun = async () => {return 5}
myFun();
// Output in the console
Promise {<fulfilled>: 5}
And you can chain with the returned result since it is a promise
Another example with enhancement as suggested in other answer
const myFun4 = async () => {
const myNum = await new Promise(r => window.setTimeout(() => r(5), 1000));
const myNum2 = await new Promise(r => window.setTimeout(() => r(5), 1000));
return myNum + myNum2;
}
myFun4().then((n) => console.log(n));
// Output
10
The return value of async-await function is Promise.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function#return_value
So, what you did actually is returning a chain of promises.
const nextPromise = () => {
console.log('next promise!');
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('next promise result')
}, 3000)
});
}
const promiseMethod = () => {
console.log('promise!');
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise result');
}, 2000)
});
}
exports.createBankAccount = functions.https.onCall((data, context) => {
return promiseMethod().then((result) => {
return nextPromise(result);
}).then((result) => {
return result;
}).catch((err) => {
// handle err
console.log(err);
})
});
exports.createBankAccountAsync = functions.https.onCall(async (data, context) => {
const result = await promiseMethod();
const res = await nextPromise(result);
return res;
});
I have created test project on firebase and both function calls give same logs.
A solution in that case is Promise.all().
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const promises = [];
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
promises.push(res1);
promises.push(res2);
// Here's the return of the promises
return Promise.all(promises).catch(error => console.error(error));
});
You may find more informations about promises in this article on freecodecamp.org/promise-all
Solution
For an alternative method, you can use Promise.allSettled(). It is the best way to wait for all promises in the function to complete as well as provide an easy way to modify the final return.
Excerpt from the documentation
The Promise.allSettled() method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise.
It is typically used when you have multiple asynchronous tasks that are not dependent on one another to complete successfully, or you'd always like to know the result of each promise.
Your updated code should be
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
return Promise.allSettled([res1, res2]).then((results) => results.forEach((result) => console.log(result.status)));
});
Additional Info
You should also look into Promise object, it has some nice methods for such a situation. Read more at documentation link
Since you need to return promise you can create the promise object and resolve/reject (return) your response from api after processing all the promises.
Option 1:
exports.createBankAccount = functions.region('europe-west1').https.onCall((data, context) => {
return new Promise(async (resolve, reject) => {
try {
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
// This will return response from api
resolve(res2);
}
catch (err) {
// Handle error here
// This will return error from api
reject(err)
}
})
});
Option 2:
exports.createBankAccount = functions.region('europe-west1').https.onCall((data, context) => {
return new Promise(async (resolve, reject) => {
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
// This will return response from api
resolve(res2);
})
.then((val) => val)
.catch((err) => {
// Handle error here
// This will return error from api
return err
})
});
Just convert to a Promise if required.
I.e. If nextPromise returns a Promise:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const res1 = await promiseMethod();
return nextPromise(res1);
});
On the other hand, if nextPromise is an async function, just convert it to a Promise:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const res1 = await promiseMethod();
return Promise.resolve(nextPromise(res1));
});
you can also convert the result:
exports.createBankAccount = functions.region('europe-west1').https.onCall(async (data, context) => {
const res1 = await promiseMethod();
const res2 = await nextPromise(res1);
return Promise.resolve(res2);
});

Categories