Returning data from promise.all() - javascript

I am trying to return data of 3 async api calls using promise.all()
function apiReq1(apiCred){
const rds = new apiCred.RDS();
var request = rds.describeDBInstances();
return request.promise();
}
function getAPIs (apiCred) {
return Promise.all([apiReq1(apiCred), apiReq2(apiCred), apiReq3(apiCred)]).then(function(data) {
console.log(data[0])
console.log(data[1])
console.log(data[2])
return data
// ideal return
//myMap.set('bar', data[0])
//.set('foo', data[1])
//.set('baz', data[2]);
//return myMap
});
}
// Function that is calling getAPIs
function getAll() {
apiCred = getApiCred()
page = getAPIs(apiCred)
console.log(page)
}
The console.log prints out the data as expected however I would like to be able to return the data object or ideally a new object with all three iterables to whatever calls getAPIs(). This is the first time I am trying to use promises and I feel there is a key async concept I am missing here on trying to return the data.

You can just do:
function getAPIs (apiCred) {
return Promise.all([apiReq1(apiCred), apiReq2(apiCred), apiReq3(apiCred)]).then(function(data) {
return {
'bar': data[0],
'foo': data[1],
'baz': data[2]
}
});
}
However, this function still returns a promise, so you cant access the result in the caller synchronously.
You need to modify your getAll method as follows
function getAll() {
apiCred = getApiCred()
return getAPIs(apiCred).then(page => {
console.log(page);
//DO YOUR THING
})
}

Related

Chrome Extension | Is there any way to make chrome.storage.local.get() return something?

in my chrome extension I need to use chrome storage. In my background script first I create an object and add it to chrome storage and then I want to get my object from there and to be returned. Something like that:
...
var obj = {};
chrome.storage.local.set(obj, function () { });
...
var data = getData(obj); // I want my object to be returned here
var returnedData = null;
function getData(obj) {
chrome.storage.local.get(obj, function(result) {
returnedData = result; // here it works, I can do something with my object
});
return returnedData; // here it doesn't work
}
As far as I understood from here chrome.storage.local.get is asynchronous with its consequences. But is there any way how to get something from chrome storage and make it to be returned? I mean maybe I should wrap chrome.storage.local.get in another function or so?
Many thanks in advance!
If you want to stay away from global variables and you're okay with modern browser requirements, then you can implement a native JavaScript Promise object. For example, here's a function that returns the stored data for a single given key:
function getData(sKey) {
return new Promise(function(resolve, reject) {
chrome.storage.local.get(sKey, function(items) {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
reject(chrome.runtime.lastError.message);
} else {
resolve(items[sKey]);
}
});
});
}
// Sample usage given this data:
// { foo: 'bar' }
getData('foo').then(function(item) {
// Returns "bar"
console.log(item);
});
If you need support for IE11 and below, then you'll have to turn to a library like jQuery.
No it's not possible
But there are several ways around this problem
Do everything you want to do with the data returned from .get() inside the callback (or start it from there using function calls). This is what #wernersbacher posted
Take a look at deferreds (jQuery or Q libraries). A deferred's promise can be returned from getData. Inside the .get() callback, you can resolve the deferred. Outside of getData you can use .then() to do something after the deferred resolved
Something like this
function getData(obj) {
var deferred = $.Deferred();
chrome.storage.local.get(obj, function(result) {
deferred.resolve(result);
});
return deferred.promise();
}
$.when(getData(obj)).then(function(data) {
// data has value of result now
};
You have to do it like that:
var returnedData = null;
function setData(value) {
returnedData = value;
}
function getData(obj) {
chrome.storage.local.get(obj, function(result) {
setData(result); // here it works, I can do something with my object
});
return; // here it doesn't work
}
..because you tried to return a value which did not get read from storage yet, so it's null.
Update with Manifest V3 :
Now chrome.storage.local.get() function returns a promise that you can chain or can await in an async function.
const storageCache = { count: 0 };
// Asynchronously retrieve data from storage.local, then cache it.
const initStorageCache = chrome.storage.local.get().then((items) => {
// Copy the data retrieved from storage into storageCache.
Object.assign(storageCache, items);
});
Note : You must omit the callback paramter to get the promise.
Reference : https://developer.chrome.com/docs/extensions/reference/storage/#:~:text=to%20callback.-,get,-function
You need to handle it with callback functions. Here are two examples. You use a single function to set, however you create a separate function for each "On Complete". You could easily modify your callback to pass additional params all the way through to perform your needed task.
function setLocalStorage(key, val) {
var obj = {};
obj[key] = val;
chrome.storage.local.set(obj, function() {
console.log('Set: '+key+'='+obj[key]);
});
}
function getLocalStorage(key, callback) {
chrome.storage.local.get(key, function(items) {
callback(key, items[key]);
});
}
setLocalStorage('myFirstKeyName', 'My Keys Value Is FIRST!');
setLocalStorage('mySecondKeyName', 'My Keys Value Is SECOND!');
getLocalStorage('myFirstKeyName', CallbackA);
getLocalStorage('mySecondKeyName', CallbackB);
// Here are a couple example callback
// functions that get executed on the
// key/val being retrieved.
function CallbackA(key, val) {
console.log('Fired In CallbackA: '+key+'='+val);
}
function CallbackB(key, val) {
console.log('Fired In CallbackA: '+key+'='+val);
}

Return from a promise then()

I have got a javascript code like this:
function justTesting() {
promise.then(function(output) {
return output + 1;
});
}
var test = justTesting();
I have got always an undefined value for the var test. I think that it is because the promises are not resolved yet..there is a way to return a value from a promise?
When you return something from a then() callback, it's a bit magic. If you return a value, the next then() is called with that value. However, if you return something promise-like, the next then() waits on it, and is only called when that promise settles (succeeds/fails).
Source: https://web.dev/promises/#queuing-asynchronous-actions
To use a promise, you have to either call a function that creates a promise or you have to create one yourself. You don't really describe what problem you're really trying to solve, but here's how you would create a promise yourself:
function justTesting(input) {
return new Promise(function(resolve, reject) {
// some async operation here
setTimeout(function() {
// resolve the promise with some value
resolve(input + 10);
}, 500);
});
}
justTesting(29).then(function(val) {
// you access the value from the promise here
log(val);
});
// display output in snippet
function log(x) {
document.write(x);
}
Or, if you already have a function that returns a promise, you can use that function and return its promise:
// function that returns a promise
function delay(t) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, t);
});
}
function justTesting(input) {
return delay(100).then(function() {
return input + 10;
});
}
justTesting(29).then(function(val) {
// you access the value from the promise here
log(val);
});
// display output in snippet
function log(x) {
document.write(x);
}
What I have done here is that I have returned a promise from the justTesting function. You can then get the result when the function is resolved.
// new answer
function justTesting() {
return new Promise((resolve, reject) => {
if (true) {
return resolve("testing");
} else {
return reject("promise failed");
}
});
}
justTesting()
.then(res => {
let test = res;
// do something with the output :)
})
.catch(err => {
console.log(err);
});
Hope this helps!
// old answer
function justTesting() {
return promise.then(function(output) {
return output + 1;
});
}
justTesting().then((res) => {
var test = res;
// do something with the output :)
}
I prefer to use "await" command and async functions to get rid of confusions of promises,
In this case I would write an asynchronous function first,
this will be used instead of the anonymous function called under "promise.then" part of this question :
async function SubFunction(output){
// Call to database , returns a promise, like an Ajax call etc :
const response = await axios.get( GetApiHost() + '/api/some_endpoint')
// Return :
return response;
}
and then I would call this function from main function :
async function justTesting() {
const lv_result = await SubFunction(output);
return lv_result + 1;
}
Noting that I returned both main function and sub function to async functions here.
Promises don't "return" values, they pass them to a callback (which you supply with .then()).
It's probably trying to say that you're supposed to do resolve(someObject); inside the promise implementation.
Then in your then code you can reference someObject to do what you want.
I think what the original poster wants is to return an unwrapped value from a promise without actually returning another promise. Unless proven otherwise, I'm afraid this is not possible outside of a then() or async/await context. You always get a promise no matter what.
You need to make use of reference data type like array or object.
function foo(u,n){
let result = [];
const userBrands = new Promise((res, rej)=> {
res(['brand 1', 'brand 3']);
})
userBrands.then((ub)=>{
return new Promise((res, rej) =>{
res([...ub, 'brand 4', 'brand 5']);
})
}).then(response => {
return result.push(...response);
});
return result;
};
foo();
You cannot return value after resolving promise. Instead call another function when promise is resolved:
function justTesting() {
promise.then(function(output) {
// instead of return call another function
afterResolve(output + 1);
});
}
function afterResolve(result) {
// do something with result
}
var test = justTesting();

Caching JavaScript promise results

I would make one call to the server to get a list of items. How do I make sure that only one call is made and the collections is processed only once to create a key value map.
var itemMap = {};
function getItems(){
getAllItemsFromServer().then(function(data){
data.forEach(value){
itemMap[value.key] = value;
}});
return itemMap;
}
//need to get the values from various places, using a loop here
//to make multiple calls
var itemKeys = ['a', 'b', 'c'];
itemKeys.forEach(key){
var value = getItems().then(function(data){ return data[key]});
console.log('item for key=' + value);
}
I'm going to add a generic method for caching a promise operation.
The trick here is that by treating the promise as a real proxy for a value and caching it and not the value we avoid the race condition. This also works for non promise functions just as well, illustrating how promises capture the concept of async + value well. I think it'd help you understand the problem better:
function cache(fn){
var NO_RESULT = {}; // unique, would use Symbol if ES2015-able
var res = NO_RESULT;
return function () { // if ES2015, name the function the same as fn
if(res === NO_RESULT) return (res = fn.apply(this, arguments));
return res;
};
}
This would let you cache any promise (or non promise operation very easily:
var getItems = cache(getAllItemsFromServer);
getItems();
getItems();
getItems(); // only one call was made, can `then` to get data.
Note that you cannot make it "synchronous".
I think what you're really looking for is
var cache = null; // cache for the promise
function getItems() {
return cache || (cache = getAllItemsFromServer().then(function(data){
var itemMap = {};
data.forEach(function(value){
itemMap[value.key] = value;
});
return itemMap;
}));
}
var itemKeys = ['a', 'b', 'c'];
itemKeys.forEach(function(key){
getItems().then(function(data){
return data[key];
}).then(function(value) {
console.log('item for key=' + value);
}); // notice that you get the value only asynchronously!
});
A promise is stateful, and as soon as it's fulfilled, its value cannot be changed. You can use .then multiple times to get its contents, and you'll get the same result every time.
The getAllItemsFromServer function returns a promise, the then block manipulates the responses and returns the itemMap, which is wrapped in a response (promise chaining). The promise is then cached and can be used to get the itemMap repeatedly.
var itemsPromise = getItems(); // make the request once and get a promise
function getItems(){
return getAllItemsFromServer().then(function(data){
return data.reduce(function(itemMap, value){
itemMap[value.key] = value;
return itemMap;
}, {});
});
}
//need to get the values from various places, using a loop here
//to make multiple calls
var itemKeys = ['a', 'b', 'c'];
itemKeys.forEach(function(key){
itemsPromise.then(function(data){
return data[key];
}).then(function(value) {
console.log('item for key=' + value);
});
});
Declare a Flag associative array, and set after the response from server and only query when flags[key] is undefined or null.
var flags = {}
itemKeys.forEach(key){
if(!flags[key]) {
var value = getItems().then(function(data){ flags[key] = true; return data[key]});
console.log('item for key=' + value);
}
}
may be you should also set flags in few other places depending on the key parameter.
Try npm dedup-async, which caches duplicated promise calls if there's a pending one, so concurrent promise calls trigger only one actual task.
const dedupa = require('dedup-async')
let evil
let n = 0
function task() {
return new Promise((resolve, reject) => {
if (evil)
throw 'Duplicated concurrent call!'
evil = true
setTimeout(() => {
console.log('Working...')
evil = false
resolve(n++)
}, 100)
})
}
function test() {
dedupa(task)
.then (d => console.log('Finish:', d))
.catch(e => console.log('Error:', e))
}
test() //Prints 'Working...', resolves 0
test() //No print, resolves 0
setTimeout(test, 200) //Prints 'Working...', resolves 1
You could also solve this by using a utils library, for an instance by using the memoizeAsync decorator from utils-decorator lib (npm install utils-decorators):
import {memoizeAsync} from 'utils-decorators';
class MyAppComponent {
#memoizeAsync(30000)
getData(input) {
...
}
}
Or by this wrapper function
import {memoizeAsyncify} from 'utils-decorators';
const methodWithCache = memoizeAsyncify(yourFunction);

Filtering and $http promises in Angular

I've got a problem with filtering data from JSON file, which is an array of 20 objects.
in my factory I have these two functions.
function getData() {
return $http
.get('mock.json')
.success(_handleData)
.error(_handleError);
}
function _handleData(data) {
var filteredData = _filterData(data, "name", "XYZ");
console.log('filteredData', filteredData);
return filteredData;
}
and here console.log("filteredData") shows only filtered elements (i.e. 3 out of 20);
next - in a service I've got this one on ng-click:
var filterMe = function () {
DataFactory
.getData(_address)
.success(_handleServiceData );
}
where
var _handleServiceData = function (data) {
filtered = data;
};
the thing is - why the 'data' in _handleServiceData shows all of the elements instead of these previously filtered?
edit: here's the plunk - results are logged in console
Because the filteredData you return from _handleData function is not passed to the success callback you attach in filterMe function. That's because you attach that callback on the very same promise, since success function doesn't create new promise like the then method does. So to solve this modify your code like this:
function getData() {
return $http
.get('mock.json')
.then(_handleData, _handleError); //use "then" instead of "success"
}
Then in filterMe function:
var filterMe = function () {
DataFactory
.getData(_address)
.then(_handleServiceData );
}
Because promises are asynchronous, and you seem to return the value of filtered to your caller before it could be assigned.
You should be doing
function getData() {
return $http
.get('mock.json')
.then(_handleData); // then (for chaining), not success!
}
var filterMe = function () {
return DataFactory
// ^^^^^^ return a promise, not assign globals in async callbacks
.getData(_address)
.catch(_handleError); // I assume you want to deal with errors only in the end
}

how can to execute multiple actions in one line?

In my case I have one repository like this from temphire (breeze)
define(['durandal/system'], function (system) {
var Repository = (function () {
var repository = function (entityManagerProvider, entityTypeName, resourceName, fetchStrategy) {
.........
this.find = function (predicate) {
var query = breeze.EntityQuery
.from(resourceName)
.where(predicate);
return executeQuery(query);
};
function executeQuery(query) {
return entityManagerProvider.manager()
.executeQuery(query.using(fetchStrategy || breeze.FetchStrategy.FromServer))
.then(function (data) { return data.results; });
}
................
};
return repository;
})();
return {
create: create,
getCtor: Repository
};
function create(entityManagerProvider, entityTypeName, resourceName, fetchStrategy) {
return new Repository(entityManagerProvider, entityTypeName, resourceName, fetchStrategy);
}
});
NOW
HOW CAN DO LIKE SOME THIS
repository.query(predicate).execute();
function query(predicate) {
return query = breeze.EntityQuery
.from(resourceName)
.where(predicate);
};
function executeQuery(query) {
return entityManagerProvider.manager().executeQuery(query.using(fetchStrategy || breeze.FetchStrategy.FromServer)).then(function(data) {
return data.results;
});
}
function execute() -- >
return executeQuery
the first action return query and after to execute
many thanks
I think the problem with what you are trying is that return terminates execution. If you want to do something as well as return in that function, then you need to do it before you return.
If, on the other hand, you really need to return the value and then execute something, then you should have the method that calls the function expecting the return, call the function to get the return value, and then have that calling function execute the thing you want executed. If that execution needs some data from the function that returns the value, then return that information with the value returned, and pass it into the function that does the execution.
Use
executeQueryLocally // This is syn
instead of
executeQuery // This is async
executeQuery sync

Categories

Resources