JS Promise doesn't chain while working chrome.storage.sync - javascript

I'm trying to make a promise chaining, but somehow the asynchronous calling using chrome.storage.sync.get always return later. Here's a correct order I thought it should be:
Create an element and append to body
Call chrome.storage.sync
Based on 2) result, trigger some event on element created in 1)
I've already tried to put new Promise in 2) step, but no successful.
The console.log always return in a wrong order. Something like:
// insertChecbox done
// start promise chrome.storage.sync
// undefined
// result asnyc: ...
Here's my attempt:
const pr = new Promise(resolve => {
this.createCheckbox();
console.log('insertChecbox done')
resolve();
});
pr
.then( resolve => {
console.log("start promise chrome.storage.sync");
return new Promise( resolve => {
return resolve(chrome.storage.sync.get(["code", "isAutocheck"], result => {
console.log('result async: ', result );
return result
}));
});
})
.then( result => {
console.log(result);
console.log('This always run before the `chrome.storage.sync` finishes loading`)
if (result.isAutocheck) {
const domCheckbox = this.getCheckboxElement();
domCheckbox && domCheckbox.click();
}
});
I wanted to separate the result of chrome.storage.sync because I need to work on other stuff, not making this callback too complex

Thanks for noticing this question, I've found an solution by changing a bit in my first .then, by explicitly resolving the result of chrome.storage.sync.get, not the whole thing.
pr
.then( () => {
return new Promise( resolve => {
return chrome.storage.sync.get(["code", "isAutocheck"], result => {
console.log('result async: ', result );
return resolve(result)
});
});
})
.then(...);

Related

Handle a reject in promise.all() in javascript [duplicate]

I have an array of Promises that I'm resolving with Promise.all(arrayOfPromises);
I go on to continue the promise chain. Looks something like this
existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
return route.handler.promiseHandler();
});
return Promise.all(arrayOfPromises)
});
existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
// do stuff with my array of resolved promises, eventually ending with a res.send();
});
I want to add a catch statement to handle an individual promise in case it errors, but when I try, Promise.all returns the first error it finds (disregards the rest), and then I can't get the data from the rest of the promises in the array (that didn't error).
I've tried doing something like ..
existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
return route.handler.promiseHandler()
.then(function(data) {
return data;
})
.catch(function(err) {
return err
});
});
return Promise.all(arrayOfPromises)
});
existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
// do stuff with my array of resolved promises, eventually ending with a res.send();
});
But that doesn't resolve.
Thanks!
--
Edit:
What the answers below said were completely true, the code was breaking due to other reasons. In case anyone is interested, this is the solution I ended up with ...
Node Express Server Chain
serverSidePromiseChain
.then(function(AppRouter) {
var arrayOfPromises = state.routes.map(function(route) {
return route.async();
});
Promise.all(arrayOfPromises)
.catch(function(err) {
// log that I have an error, return the entire array;
console.log('A promise failed to resolve', err);
return arrayOfPromises;
})
.then(function(arrayOfPromises) {
// full array of resolved promises;
})
};
API Call (route.async call)
return async()
.then(function(result) {
// dispatch a success
return result;
})
.catch(function(err) {
// dispatch a failure and throw error
throw err;
});
Putting the .catch for Promise.all before the .then seems to have served the purpose of catching any errors from the original promises, but then returning the entire array to the next .then
Thanks!
Promise.all is all or nothing. It resolves once all promises in the array resolve, or reject as soon as one of them rejects. In other words, it either resolves with an array of all resolved values, or rejects with a single error.
Some libraries have something called Promise.when, which I understand would instead wait for all promises in the array to either resolve or reject, but I'm not familiar with it, and it's not in ES6.
Your code
I agree with others here that your fix should work. It should resolve with an array that may contain a mix of successful values and errors objects. It's unusual to pass error objects in the success-path but assuming your code is expecting them, I see no problem with it.
The only reason I can think of why it would "not resolve" is that it's failing in code you're not showing us and the reason you're not seeing any error message about this is because this promise chain is not terminated with a final catch (as far as what you're showing us anyway).
I've taken the liberty of factoring out the "existing chain" from your example and terminating the chain with a catch. This may not be right for you, but for people reading this, it's important to always either return or terminate chains, or potential errors, even coding errors, will get hidden (which is what I suspect happened here):
Promise.all(state.routes.map(function(route) {
return route.handler.promiseHandler().catch(function(err) {
return err;
});
}))
.then(function(arrayOfValuesOrErrors) {
// handling of my array containing values and/or errors.
})
.catch(function(err) {
console.log(err.message); // some coding error in handling happened
});
NEW ANSWER
const results = await Promise.all(promises.map(p => p.catch(e => e)));
const validResults = results.filter(result => !(result instanceof Error));
FUTURE Promise API
Chrome 76: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
You can download https://www.npmjs.com/package/promise.allsettled to get it now. In certain browsers allSettled comes preinstalled with the browser itself. It's worth downloading the package for peace of mind because eg. TypeScript doesn't have default definitions for allSettled.
ES2020 introduces new method for the Promise type: Promise.allSettled().
Promise.allSettled gives you a signal when all the input promises are settled, which means they’re either fulfilled or rejected. This is useful in cases where you don’t care about the state of the promise, you just want to know when the work is done, regardless of whether it was successful.
async function() {
const promises = [
fetch('/api.stackexchange.com/2.2'), // succeeds
fetch('/this-will-fail') // fails
];
const result = await Promise.allSettled(promises);
console.log(result.map(promise => promise.status));
// ['fulfilled', 'rejected']
}
Read more in the v8 blog post.
To continue the Promise.all loop (even when a Promise rejects) I wrote a utility function which is called executeAllPromises. This utility function returns an object with results and errors.
The idea is that all Promises you pass to executeAllPromises will be wrapped into a new Promise which will always resolve. The new Promise resolves with an array which has 2 spots. The first spot holds the resolving value (if any) and the second spot keeps the error (if the wrapped Promise rejects).
As a final step the executeAllPromises accumulates all values of the wrapped promises and returns the final object with an array for results and an array for errors.
Here is the code:
function executeAllPromises(promises) {
// Wrap all Promises in a Promise that will always "resolve"
var resolvingPromises = promises.map(function(promise) {
return new Promise(function(resolve) {
var payload = new Array(2);
promise.then(function(result) {
payload[0] = result;
})
.catch(function(error) {
payload[1] = error;
})
.then(function() {
/*
* The wrapped Promise returns an array:
* The first position in the array holds the result (if any)
* The second position in the array holds the error (if any)
*/
resolve(payload);
});
});
});
var errors = [];
var results = [];
// Execute all wrapped Promises
return Promise.all(resolvingPromises)
.then(function(items) {
items.forEach(function(payload) {
if (payload[1]) {
errors.push(payload[1]);
} else {
results.push(payload[0]);
}
});
return {
errors: errors,
results: results
};
});
}
var myPromises = [
Promise.resolve(1),
Promise.resolve(2),
Promise.reject(new Error('3')),
Promise.resolve(4),
Promise.reject(new Error('5'))
];
executeAllPromises(myPromises).then(function(items) {
// Result
var errors = items.errors.map(function(error) {
return error.message
}).join(',');
var results = items.results.join(',');
console.log(`Executed all ${myPromises.length} Promises:`);
console.log(`— ${items.results.length} Promises were successful: ${results}`);
console.log(`— ${items.errors.length} Promises failed: ${errors}`);
});
Promise.allSettled
Instead of Promise.all use Promise.allSettled which waits for all promises to settle, regardless of the result
let p1 = new Promise(resolve => resolve("result1"));
let p2 = new Promise( (resolve,reject) => reject('some troubles') );
let p3 = new Promise(resolve => resolve("result3"));
// It returns info about each promise status and value
Promise.allSettled([p1,p2,p3]).then(result=> console.log(result));
Polyfill
if (!Promise.allSettled) {
const rejectHandler = reason => ({ status: 'rejected', reason });
const resolveHandler = value => ({ status: 'fulfilled', value });
Promise.allSettled = function (promises) {
const convertedPromises = promises
.map(p => Promise.resolve(p).then(resolveHandler, rejectHandler));
return Promise.all(convertedPromises);
};
}
As #jib said,
Promise.all is all or nothing.
Though, you can control certain promises that are "allowed" to fail and we would like to proceed to .then.
For example.
Promise.all([
doMustAsyncTask1,
doMustAsyncTask2,
doOptionalAsyncTask
.catch(err => {
if( /* err non-critical */) {
return
}
// if critical then fail
throw err
})
])
.then(([ mustRes1, mustRes2, optionalRes ]) => {
// proceed to work with results
})
Using Async await -
here one async function func1 is returning a resolved value, and func2 is throwing a error and returning a null in this situation, we can handle it how we want and return accordingly.
const callingFunction = async () => {
const manyPromises = await Promise.all([func1(), func2()]);
console.log(manyPromises);
}
const func1 = async () => {
return 'func1'
}
const func2 = async () => {
try {
let x;
if (!x) throw "x value not present"
} catch(err) {
return null
}
}
callingFunction();
Output is - [ 'func1', null ]
if you get to use the q library https://github.com/kriskowal/q
it has q.allSettled() method that can solve this problem
you can handle every promise depending on its state either fullfiled or rejected
so
existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
return route.handler.promiseHandler();
});
return q.allSettled(arrayOfPromises)
});
existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
//so here you have all your promises the fulfilled and the rejected ones
// you can check the state of each promise
arrayResolved.forEach(function(item){
if(item.state === 'fulfilled'){ // 'rejected' for rejected promises
//do somthing
} else {
// do something else
}
})
// do stuff with my array of resolved promises, eventually ending with a res.send();
});
For those using ES8 that stumble here, you can do something like the following, using async functions:
var arrayOfPromises = state.routes.map(async function(route){
try {
return await route.handler.promiseHandler();
} catch(e) {
// Do something to handle the error.
// Errored promises will return whatever you return here (undefined if you don't return anything).
}
});
var resolvedPromises = await Promise.all(arrayOfPromises);
Promise.allSettled with a filter
const promises = [
fetch('/api-call-1'),
fetch('/api-call-2'),
fetch('/api-call-3'),
];
// Imagine some of these requests fail, and some succeed.
const resultFilter = (result, error) => result.filter(i => i.status === (!error ? 'fulfilled' : 'rejected')).map(i => (!error ? i.value : i.reason));
const result = await Promise.allSettled(promises);
const fulfilled = resultFilter(result); // all fulfilled results
const rejected = resultFilter(result, true); // all rejected results
Have you considered Promise.prototype.finally()?
It seems to be designed to do exactly what you want - execute a function once all the promises have settled (resolved/rejected), regardless of some of the promises being rejected.
From the MDN documentation:
The finally() method can be useful if you want to do some processing or cleanup once the promise is settled, regardless of its outcome.
The finally() method is very similar to calling .then(onFinally, onFinally) however there are couple of differences:
When creating a function inline, you can pass it once, instead of being forced to either declare it twice, or create a variable for it.
A finally callback will not receive any argument, since there's no reliable means of determining if the promise was fulfilled or rejected. This use case is for precisely when you do not care about the rejection reason, or the fulfillment value, and so there's no need to provide it.
Unlike Promise.resolve(2).then(() => {}, () => {}) (which will be resolved with undefined), Promise.resolve(2).finally(() => {}) will be resolved with 2.
Similarly, unlike Promise.reject(3).then(() => {}, () => {}) (which will be fulfilled with undefined), Promise.reject(3).finally(() => {}) will be rejected with 3.
== Fallback ==
If your version of JavaScript doesn't support Promise.prototype.finally() you can use this workaround from Jake Archibald: Promise.all(promises.map(p => p.catch(() => undefined)));
We can handle the rejection at the individual promises level, so when we get the results in our result array, the array index which has been rejected will be undefined. We can handle that situation as needed, and use the remaining results.
Here I have rejected the first promise, so it comes as undefined, but we can use the result of the second promise, which is at index 1.
const manyPromises = Promise.all([func1(), func2()]).then(result => {
console.log(result[0]); // undefined
console.log(result[1]); // func2
});
function func1() {
return new Promise( (res, rej) => rej('func1')).catch(err => {
console.log('error handled', err);
});
}
function func2() {
return new Promise( (res, rej) => setTimeout(() => res('func2'), 500) );
}
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
let sum = 0;
let promiseErrorArr = [];
Promise.allSettled(promises)
.then((results) => {
results.forEach(result => {
if (result.status === "rejected") {
sum += 1;
promiseErrorArr.push(result)
}
})
return ( (sum>0) ? promiseFailed() : promisePassed())
})
function promiseFailed(){
console.log('one or all failed!')
console.log(promiseErrorArr)
}
function promisePassed(){
console.log('all passed!')
}
// expected output:
// "one or all failed!"
// Array [Object { status: "rejected", reason: "foo" }]
Alternately, if you have a case where you don't particularly care about the values of the resolved promises when there is one failure but you still want them to have run, you could do something like this which will resolve with the promises as normal when they all succeed and reject with the failed promises when any of them fail:
function promiseNoReallyAll (promises) {
return new Promise(
async (resolve, reject) => {
const failedPromises = []
const successfulPromises = await Promise.all(
promises.map(
promise => promise.catch(error => {
failedPromises.push(error)
})
)
)
if (failedPromises.length) {
reject(failedPromises)
} else {
resolve(successfulPromises)
}
}
)
}
You can always wrap your promise returning functions in a way that they catches failure and returning instead an agreed value (e.g. error.message), so the exception won't roll all the way up to the Promise.all function and disable it.
async function resetCache(ip) {
try {
const response = await axios.get(`http://${ip}/resetcache`);
return response;
}catch (e) {
return {status: 'failure', reason: 'e.message'};
}
}
I've found a way (workaround) to do this without making it sync.
So as it was mentioned before Promise.all is all of none.
so... Use an enclosing promise to catch and force resolve.
let safePromises = originalPrmises.map((imageObject) => {
return new Promise((resolve) => {
// Do something error friendly
promise.then(_res => resolve(res)).catch(_err => resolve(err))
})
})
})
// safe
return Promise.all(safePromises)
You would need to know how to identify an error in your results. If you do not have a standard expected error, I suggest that you run a transformation on each error in the catch block that makes it identifiable in your results.
try {
let resArray = await Promise.all(
state.routes.map(route => route.handler.promiseHandler().catch(e => e))
);
// in catch(e => e) you can transform your error to a type or object
// that makes it easier for you to identify whats an error in resArray
// e.g. if you expect your err objects to have e.type, you can filter
// all errors in the array eg
// let errResponse = resArray.filter(d => d && d.type === '<expected type>')
// let notNullResponse = resArray.filter(d => d)
} catch (err) {
// code related errors
}
Not the best way to error log, but you can always set everything to an array for the promiseAll, and store the resulting results into new variables.
If you use graphQL you need to postprocess the response regardless and if it doesn't find the correct reference it'll crash the app, narrowing down where the problem is at
const results = await Promise.all([
this.props.client.query({
query: GET_SPECIAL_DATES,
}),
this.props.client.query({
query: GET_SPECIAL_DATE_TYPES,
}),
this.props.client.query({
query: GET_ORDER_DATES,
}),
]).catch(e=>console.log(e,"error"));
const specialDates = results[0].data.specialDates;
const specialDateTypes = results[1].data.specialDateTypes;
const orderDates = results[2].data.orders;
Unfortunately, I don't have enough reputation to comment (or do much of anything, really), so I'm posting this as an answer in response to Eric's answer here.
The executor function can also be an async function. However, this is usually a mistake, for a few reasons:
If an async executor function throws an error, the error will be lost and won’t cause the newly-constructed Promise to reject. This could make it difficult to debug and handle some errors.
If a Promise executor function is using await, this is usually a sign that it is not actually necessary to use the new Promise constructor, or the scope of the new Promise constructor can be reduced.
From this explanation as to why Promises should not utilize an async executor function
Instead, you should opt for Promise.allSettled(), as suggested here by Asaf.
With the help of allSettled,we can now read the status of
each promise is, and process each error individually, without losing any of this critical information
const promises = [
fetch('/api/first'), // first
fetch('/api/second') // second
];
The simplest way is to handle errors
const [firstResult, secondResult] = await Promise.allSettled(promises)
// Process first
if (firstResult.status === 'rejected') {
const err = firstResult.reason
// Here you can handle error
} else {
const first = firstResult.value
}
// Process second
if (secondResult.status === 'rejected') {
const err = secondResult.reason
// Here you can handle error
} else {
const second = secondResult.value
}
A nice way to handle error
const results = await Promise.allSettled(promises);
const [first, second] = handleResults(results)
function handleResults(results) {
const errors = results.filter(result => result.status === 'rejected').map(result => result.reason)
if (errors.length) {
// Aggregate all errors into one
throw new AggregateError(errors)
}
return results.map(result => result.value)
}
That's how Promise.all is designed to work. If a single promise reject()'s, the entire method immediately fails.
There are use cases where one might want to have the Promise.all allowing for promises to fail. To make this happen, simply don't use any reject() statements in your promise. However, to ensure your app/script does not freeze in case any single underlying promise never gets a response, you need to put a timeout on it.
function getThing(uid,branch){
return new Promise(function (resolve, reject) {
xhr.get().then(function(res) {
if (res) {
resolve(res);
}
else {
resolve(null);
}
setTimeout(function(){reject('timeout')},10000)
}).catch(function(error) {
resolve(null);
});
});
}
I wrote a npm library to deal with this problem more beautiful.
https://github.com/wenshin/promiseallend
Install
npm i --save promiseallend
2017-02-25 new api, it's not break promise principles
const promiseAllEnd = require('promiseallend');
const promises = [Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)];
const promisesObj = {k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)};
// input promises with array
promiseAllEnd(promises, {
unhandledRejection(error, index) {
// error is the original error which is 'error'.
// index is the index of array, it's a number.
console.log(error, index);
}
})
// will call, data is `[1, undefined, 2]`
.then(data => console.log(data))
// won't call
.catch(error => console.log(error.detail))
// input promises with object
promiseAllEnd(promisesObj, {
unhandledRejection(error, prop) {
// error is the original error.
// key is the property of object.
console.log(error, prop);
}
})
// will call, data is `{k1: 1, k3: 2}`
.then(data => console.log(data))
// won't call
.catch(error => console.log(error.detail))
// the same to `Promise.all`
promiseAllEnd(promises, {requireConfig: true})
// will call, `error.detail` is 'error', `error.key` is number 1.
.catch(error => console.log(error.detail))
// requireConfig is Array
promiseAllEnd(promises, {requireConfig: [false, true, false]})
// won't call
.then(data => console.log(data))
// will call, `error.detail` is 'error', `error.key` is number 1.
.catch(error => console.log(error.detail))
// requireConfig is Array
promiseAllEnd(promises, {requireConfig: [true, false, false]})
// will call, data is `[1, undefined, 2]`.
.then(data => console.log(data))
// won't call
.catch(error => console.log(error.detail))
————————————————————————————————
Old bad api, do not use it!
let promiseAllEnd = require('promiseallend');
// input promises with array
promiseAllEnd([Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)])
.then(data => console.log(data)) // [1, undefined, 2]
.catch(error => console.log(error.errorsByKey)) // {1: 'error'}
// input promises with object
promiseAllEnd({k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)})
.then(data => console.log(data)) // {k1: 1, k3: 2}
.catch(error => console.log(error.errorsByKey)) // {k2: 'error'}

Am I chaining Promises correctly or committing a sin?

I have not worked with Javascript in a long time, so now promises are a new concept to me. I have some operations requiring more than one asynchronous call but which I want to treat as a transaction where steps do not execute if the step before failed. Currently I chain promises by nesting and I want to return a promise to the caller.
After reading the chaining section of Mozilla's Using Promises guide, I'm not sure if what I'm doing is correct or equivalent to the "callback pyramid of doom".
Is there a cleaner way to do this (besides chaining with a guard check in each then)? Am I right in my belief that in Mozilla's example it will execute each chained then even when there is an error?
myfunction(key) => {
return new Promise((outerResolve, outerReject) => {
return new Promise((resolve, reject) => {
let item = cache.get(key);
if (item) {
resolve(item);
} else {
//we didnt have the row cached, load it from store
chrome.storage.sync.get(key, function (result) {
chrome.runtime.lastError
? reject({ error: chrome.runtime.lastError.message })
: resolve(result);
});
}
}).then((resolve) => {
//Now the inner most item is resolved, we are working in the 'outer' shell
if (resolve.error) {
outerReject(resolve);
} else {
//No error, continue
new Promise((resolve, reject) => {
chrome.storage.sync.get(keyBasedOnPreviousData, function (result) {
chrome.runtime.lastError
? reject({ error: chrome.runtime.lastError.message })
: resolve(result);
});
}).then((resolve) => {
//finally return the result to the caller
if (resolve.error) {
outerReject(resolve);
} else {
outerResolve(resolve);
}
});
}
});
});
}
Subsequent then statements are not executed (until a catch) when an exception is thrown. Also, .then returns a Promise, so you don't need to create an additional, outer Promise.
Try this example:
var p = new Promise((resolve, reject) => {
console.log('first promise, resolves');
resolve();
})
.then(() => {
throw new Error('Something failed');
})
.then(() => {
console.log('then after the error');
return('result');
});
p.then(res => console.log('success: ' + res), err => console.log('error: ' + err));
You will not see "then after the error" in the console, because that happens after an exception is thrown. But if you comment the throw statement, you will get the result you expect in the Promise.
I am not sure I understand your example entirely, but I think it could be simplified like this:
myfunction(key) => {
return new Promise((resolve, reject) => {
let item = cache.get(key);
if (item) {
resolve(item);
} else {
//we didnt have the row cached, load it from store
chrome.storage.sync.get(key, function (result) {
chrome.runtime.lastError
? throw new Error(chrome.runtime.lastError.message)
: resolve(result);
});
}
}).then((previousData) => {
// keyBasedOnPreviousData is calculated based on previousData
chrome.storage.sync.get(keyBasedOnPreviousData, function (result) {
chrome.runtime.lastError
? throw new Error(chrome.runtime.lastError.message)
: return result;
});
});
}
It's a bit of a mess. This is my attempt at rewriting. A good thing to try to avoid is new Promise().
function chromeStorageGet(key) {
return new Promise( (res, rej) => {
chrome.storage.sync.get(key, result => {
if (chrome.runtime.lastError) {
rej(new Error(chrome.runtime.lastError.message))
} else {
res(result)
}
});
});
});
function myfunction(key) {
const item = cache.get(key) ? Promise.resolve(cache.get(key)) : chromeStorageGet(key);
return item.then( cacheResult => {
return chromeStorageGet(keyBasedOnPreviousData);
});
}
Why avoid new Promise()?
The reason for this is that you want to do every step with then(). If any error happened in any of the promises, every promise in the chain will fail and any subsequent then() will not get executed until there is a catch() handler.
Lots of promise based-code requires no error handlers, because promise-based functions always return promises and exceptions should flow all the back to the caller until there is something useful to be done with error handling.
Note that the exceptions to these 2 rules are in my chromeStorageGet function. A few notes here:
new Promise can be a quick and easy way to convert callback code to promise code.
It's usually a good idea to just create a little conversion layer for this callback-based code. If you need chrome.storage.sync in other places, maybe create a little utility that promisifies all its functions.
If there is only 1 'flow', you can just use a series of then() to complete the process, but sometimes you need to conditionally do other things. Just splitting up these complicated operations in a number of different functions can really help here.
But this:
const result = condition ? Promise.resolve() : Promise.reject();
Is almost always preferred to:
const result = new Promise( (res, rej) => {
if (condition) {
res();
} else {
rej();
}
}

Promise with Javascript

I'm struggling to understand Promise but it's really difficult for me.
it makes me crazy, I would appreciate your helps.
this is a code and I want "console.log(data)" after Recommend function finished
but the result is undefined.
What should I do?
many thanks
This is app.js
var _promise = function () {
return new Promise((resolve,reject) => {
var data = getJS.Recommend(req.query.User_id)
resolve(data)
}).then(function(data) {
console.log(data)
})
_promise();
}})
this is RecommendPost.js
exports.Recommend =(myId)=>{
var posts=[]
User.find({
User_id : myId
}).then((result)=>{
return User.find()
.select('User_id')
.where('age').equals(result[0].age)
}).then((User_id)=>{
return Promise.all(User_id.map((user,idx,arr)=>{
return Count.find()
.select('board_id')
.where('User_id').equals(user.User_id)
}))
}).then((Users_id)=>{
Users_id.forEach(items=>{
items.forEach(post=>{
posts.push(post.board_id)
})
})
}).then(()=>{
return getMax(posts);
})
}
cf. In RecommendPost.js, the posts works synchronously
//-------- I solved this problem! as some guys said, Recommend function should return Promise. So I edited, then this worked !
this is edited code. thank you for helping me :)
This is app.js
var _promise = function () {
return new Promise((resolve, reject) => {
getJS.Recommend(req.query.User_id).then((data) => {
resolve(data);
})
})
}
_promise().then((data) => { console.log(data) });
this is RecommendPost.js
exports.Recommend =(myId)=>{
return new Promise((resolve,reject)=>{
var posts=[]
User.find({
User_id : myId
}).then((result)=>{
return User.find()
.select('User_id')
.where('age').equals(result[0].age)
}).then((User_id)=>{
return Promise.all(User_id.map((user,idx,arr)=>{
return Count.find()
.select('board_id')
.where('User_id').equals(user.User_id)
}))
}).then((Users_id)=>{
Users_id.forEach(items=>{
items.forEach(post=>{
posts.push(post.board_id)
})
})
}).then(()=>{
resolve (getMax(posts));
})
})
}
It might be clearer for you with async/await that makes asynchronous code look like synchronous:
async function main() {
var data = await getJS.Recommend(req.query.User_id); // waits till its done before going to the next line
console.log(data);
}
// unimportant specific implementation details for live example:
const getJS = {
Recommend(id) {
data = "some data for user ";
return new Promise((res) => setTimeout(() => res(data + id), 1000));
}
}
const req = {
query: {
User_id: 5
}
}
main();
When you call to resolve function is mean that the function is finish and it returns to the caller.
the data object in the then doesn't exist
The Promise.resolve(value) method returns a Promise object that is
resolved with the given value. If the value is a promise, that promise
is returned; if the value is a thenable (i.e. has a "then" method),
the returned promise will "follow" that thenable, adopting its
eventual state; otherwise the returned promise will be fulfilled with
the value.
ES5 :
var _promise = function (req) {
return new Promise((resolve,reject) => {
getJS.Recommend(req.query.User_id).then((data)=>{
console.log(data)
resolve(data)
},(err)=>{
console.log(err)
reject(err);
})
})
}
_promise(req);
ES6
async function _promise(req) {
return await getJS.Recommend(req.query.User_id)
}
For your edit(The entire code):
In this case that Recommend function returns a promise, you don't need the _promise function that wraps it with another one.
and you call it direct
getJS.Recommend(req.query.User_id).then(
(data) => {
console.log(data)
});
Promises means it will always return something, either error or success.
if you resolve something then it will return from there. so your .then part will not execute.
so if you are using resolve and reject params in function then, use .catch method.
return new Promise((resolve,reject) => {
var data = getJS.Recommend(req.query.User_id)
resolve(data)
}) .catch(function (ex) { // in case of error
console.log(ex);
})

What are some typical use-cases for `Promise.race()`? [duplicate]

As far as I know, there are two options about promise:
promise.all()
promise.race()
Ok, I know what promise.all() does. It runs promises in parallel, and .then gives you the values if both resolved successfully. Here is an example:
Promise.all([
$.ajax({ url: 'test1.php' }),
$.ajax({ url: 'test2.php' })
])
.then(([res1, res2]) => {
// Both requests resolved
})
.catch(error => {
// Something went wrong
});
But I don't understand what does promise.race() is supposed to do exactly? In other word, what's the difference with not using it? Assume this:
$.ajax({
url: 'test1.php',
async: true,
success: function (data) {
// This request resolved
}
});
$.ajax({
url: 'test2.php',
async: true,
success: function (data) {
// This request resolved
}
});
See? I haven't used promise.race() and it behaves like promise.race(). Anyway, is there any simple and clean example to show me when exactly should I use promise.race() ?
As you see, the race() will return the promise instance which is firstly resolved or rejected:
var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, 'one');
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'two');
});
Promise.race([p1, p2]).then(function(value) {
console.log(value); // "two"
// Both resolve, but p2 is faster
});
For a scenes to be used, maybe you want to limit the cost time of a request :
var p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
])
p.then(response => console.log(response))
p.catch(error => console.log(error))
With the race() you just need to get the returned promise, you needn't care about which one of the promises in the race([]) firstly returned,
However, without the race, just like your example, you need to care about which one will firstly returned, and called the callback in the both success callback.
I've used it for request batching. We had to batch tens of thousands of records into batches for a long running execution. We could do it in parallel, but didn't want the number of pending requests to get out of hand.
Race lets us keep a fixed number of parallel promises running and add one to replace whenever one completes
const _ = require('lodash')
async function batchRequests(options) {
let query = { offset: 0, limit: options.limit };
do {
batch = await model.findAll(query);
query.offset += options.limit;
if (batch.length) {
const promise = doLongRequestForBatch(batch).then(() => {
// Once complete, pop this promise from our array
// so that we know we can add another batch in its place
_.remove(promises, p => p === promise);
});
promises.push(promise);
// Once we hit our concurrency limit, wait for at least one promise to
// resolve before continuing to batch off requests
if (promises.length >= options.concurrentBatches) {
await Promise.race(promises);
}
}
} while (batch.length);
// Wait for remaining batches to finish
return Promise.all(promises);
}
batchRequests({ limit: 100, concurrentBatches: 5 });
It's a piece to build a timeout system, where:
the request/computation may be canceled by another channel
it will still be used later, but we need an interaction now.
For an example of the second, one might show a spinner "instantly" while still defaulting to show real content if it comes in fast enough. Try running the below a few times - note at least some console message comes "instantly". This might normally be attached to perform operations on a UI.
The key to note is - the result of Promise.race is much less important than the side effects (though, this then is a code smell).
// 300 ms _feels_ "instant", and flickers are bad
function getUserInfo(user) {
return new Promise((resolve, reject) => {
// had it at 1500 to be more true-to-life, but 900 is better for testing
setTimeout(() => resolve("user data!"), Math.floor(900*Math.random()));
});
}
function showUserInfo(user) {
return getUserInfo().then(info => {
console.log("user info:", info);
return true;
});
}
function showSpinner() {
console.log("please wait...")
}
function timeout(delay, result) {
return new Promise(resolve => {
setTimeout(() => resolve(result), delay);
});
}
Promise.race([showUserInfo(), timeout(300)]).then(displayed => {
if (!displayed) showSpinner();
});
Inspiration credit to a comment by captainkovalsky.
An example of the first:
function timeout(delay) {
let cancel;
const wait = new Promise(resolve => {
const timer = setTimeout(() => resolve(false), delay);
cancel = () => {
clearTimeout(timer);
resolve(true);
};
});
wait.cancel = cancel;
return wait;
}
function doWork() {
const workFactor = Math.floor(600*Math.random());
const work = timeout(workFactor);
const result = work.then(canceled => {
if (canceled)
console.log('Work canceled');
else
console.log('Work done in', workFactor, 'ms');
return !canceled;
});
result.cancel = work.cancel;
return result;
}
function attemptWork() {
const work = doWork();
return Promise.race([work, timeout(300)])
.then(done => {
if (!done)
work.cancel();
return (done ? 'Work complete!' : 'I gave up');
});
}
attemptWork().then(console.log);
You can see from this one that the timeout's console.log is never executed when the timeout hits first. It should fail/succeed about half/half, for testing convenience.
Here's an easy example to understand the use of promise.race():
Imagine you need to fetch some data from a server and if the data takes too long to load (say 15 seconds) you want to show an error.
You would call promise.race() with two promises, the first being your ajax request and the second being a simple setTimeout(() => resolve("ERROR"), 15000)
Summary:
Promise.race is a JS built in function that accepts an iterable of Promises (e.g. Array) as an argument. This function then asynchronously returns a Promise as soon as one in of the Promises passed in the iterable is either resolved or rejected.
Example 1:
var promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('Promise-one'), 500);
});
var promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve('Promise-two'), 100);
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value);
// Both resolve, but promise2 is faster than promise 1
});
In this example first an array of Promises is passed in Promise.race. Both of the promises resolve but promise1 resolves faster. Therefore the promise is resolved with the value of promise1, which is the string 'Promise-one'.
Example 2:
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve('succes'), 2000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => reject('err'), 1000);
});
Promise.race([promise1, promise2])
.then((value) => {
console.log(value);
}).catch((value) => {
console.log('error: ' + value);
});
In this second example the second promise rejects faster than the first promise can resolve. Therefore Promise.race will return a rejected promise with the value of 'err' which was the value that Promise2 rejected with.
The key point to understand is that Promice.race takes an iterable of Promises and returns a Promise based on the first resolved or rejected promise in that iterable (with the corresponding resolve() or reject() values).
Let's take an sample workaround of Promise.race like below.
const race = (promises) => {
return new Promise((resolve, reject) => {
return promises.forEach(f => f.then(resolve).catch(reject));
})
};
You can see race function executes all promises, but whomever finishes first will resolve/reject with wrapper Promise.

Response terminates before the result could be collected from the promise

In the following snippet, lineReader listens to an event line. When a line event is received, it calls da on Parser which returns a Promise
lineReader.on('line',(line) => {
Parser.da(line).then((info) => {
});
});
lineReader.on('close',() => {
req.end();
});
Parser.da = function(line) {
return new Promise((resolve,reject) => {
geo.getLocation().then((r) => {
console.log(r); return resolve(r);
});
});
}
da function in return calls a function which also operates on Promise. What happens is that I can never see the output from geo.getLocation and readLine.on('close') gets called.
What should be the way to handle this situation?
You are not resolving the promise. When you get the result from geo service you need to resolve the data.
Take a look this
Parser.da = function(line) {
return new Promise((resolve,reject) => {
geo.getLocation().then((r) => {
console.log(r);
resolve(r);
});
});
}
Why do you not just return the Promise from geo.geoLocation() instead of wrapping it into another promise? Like this:
Parser.da = function(line) {
return geo.geoLocation();
}
Or you might want to chain "then"'s instead. Although this is synchronous
Parser.da = function(line) {
return geo.geoLocation().then(function (r) {
console.log(r);
return r; // return r to chain it to the next ".then"
});
}
One possible problem might be that a promise is asynchronous so the lineReader.on("line"); event is closed before the promise can be executed because the event is synchronous. And then the lineReader.on("close"); is executed before the promise is resolved.
Also you should always have a "catch" in your promise so you can see if there are any errors. Like this:
lineReader.on('line',(line) => {
Parser.da(line).then((info) => {
// ...do code here
}, console.error); // errors will be outputted to console
});

Categories

Resources