This question already has answers here:
How do I access previous promise results in a .then() chain?
(17 answers)
Closed 6 years ago.
I've promise chain which the response in the first chain should be used later on in the chain (in 4 & 6) places, I use some global variable to handle it but this is not the right way ,there is a better way to achieve this with promise?
This is some illustration for the issue...
var step1 = (ms) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
log("This is step 1");
resolve(20);
}, ms);
})
}
var step2 = (ms) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
log("This is step 2");
resolve();
}, ms);
})
};
var step3 = (ms) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
log("This is step 3");
resolve();
}, ms);
})
};
var step4 = (ms) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
log("Step 4, run : " + ms );
resolve();
}, ms);
})
};
var globalVar = null;
//Promise chain
step1(500)
.then((res) => {
//Here I keep the response in global variable to use later on
globalVar = res;
log(res);
return step2(300);
}).then(() => {
return step3(200);
}).then(() =>{
//Here I need to use the res from the first promise
var lclvar = globalVar +200 ;
return step4(lclvar);
}).catch((err) => {
log(err);
});
I found this but this is not helping in this case(at least didn't able to handle it)
How do I access previous promise results in a .then() chain?
You could just nest the step2, step3, etc calls inside the first handler, then res would be available to them all
step1(500).then((res) => {
log(res);
return step2(300)
.then(() => step3(200))
.then(() => step4(res + 200));
}).catch((err) => {
log(err);
});
If you really wan to chain this actually words. the idea is a bus parameter traveling along the way.It's created in the first promise as a closure.
you can run this code:
log=console.log
var step1 = (ms) => {
var Buss={p1:20};
return new Promise((resolve, reject) => {
setTimeout(() => {
log("This is step 1");
resolve(Buss);
}, ms);
})
}
var step2 = (ms,bus) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
log("This is step 2");
resolve(bus);
}, ms);
})
};
var step3 = (ms,bus) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
log("This is step 3");
resolve(bus);
}, ms);
})
};
var step4 = (bus) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
log("Step 4, run : " + bus.p1 );
log("bus arrives:",bus);
resolve(bus);
}, bus.p1);
})
};
//Promise chain
step1(500)
.then((res) => {
//Here I keep the response in global variable to use later on
//globalVar = res;
log(res);
return step2(300,res);
}).then((res) => {
return step3(200,res);
}).then((res) =>{
//Here I need to use the res from the first promise
//var lclvar = globalVar +200 ;
res.p1+=200;
return step4(res);
}).catch((err) => {
log(err);
});
Related
i have a promise chain
If i receive error in getServiceCost I want to repeat the chain again (retry) for 2 times how can i achieve this when using Promise chain , meaning again execute getUser, getServiceCost
getUser(100)
.then(getServices)
.then(getServiceCost)
.then(console.log);
function getUser(userId) {
return new Promise((resolve, reject) => {
console.log('Get the user from the database.');
setTimeout(() => {
resolve({
userId: userId,
username: 'admin'
});
}, 1000);
})
}
function getServices(user) {
return new Promise((resolve, reject) => {
console.log(`Get the services of ${user.username} from the API.`);
setTimeout(() => {
resolve(['Email', 'VPN', 'CDN']);
}, 3 * 1000);
});
}
function getServiceCost(services) {
return new Promise((resolve, reject) => {
console.log(`Calculate the service cost of ${services}.`);
setTimeout(() => {
resolve(services.length * 100);
}, 2 * 1000);
});
}
If i receive error in getServiceCost I want to repeat the chain again (retry) for 2 times how can i achieve this when using Promise chain , meaning again execute
getUser, getServiceCost
I'd use an async function (all modern environments support them, and you can transpile for obsolete environments), which lets you use a simple loop. Perhaps as a utility function you can reuse:
async function callWithRetry(fn, retries = 3) {
while (retries-- > 0) {
try {
return await fn();
} catch (error) {
if (retries === 0) {
throw error;
}
}
}
return new Error(`Out of retries`); // Probably using an `Error` subclass
}
Using it:
callWithRetry(() => getUser(100).then(getServices).then(getServiceCost))
.then(console.log)
.catch(error => { /*...handle/report error...*/ });
Or
callWithRetry(async () => {
const user = await getUser(100);
const services = await getServices(user);
return await getServiceCost(services);
})
.then(console.log)
.catch(error => { /*...handle/report error...*/ });
I am trying to write a polyfill for Promise.all, it is working fine when I passed promises without setTimeout, but with setTimeout, I am not getting the correct results since it resolves and returns the promise before the timer expires.
How can this case be handled to work in the below function, in the same way as actual Promise.all works.
Below is my code snippet and link to codesandbox
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("success 1"), 2000);
});
const promise2 = new Promise((resolve, reject) => {
resolve("success 2");
});
const promise3 = new Promise((resolve, reject) => {
resolve("success 3");
});
function resolveAll(promiseArr) {
let arr = [];
return new Promise((resolve, reject) => {
promiseArr.map((each_promise, index) => {
return each_promise
.then((res) => {
arr[index] = res;
if (index === promiseArr.length - 1) {
resolve(arr);
}
})
.catch((err) => {
reject(err);
});
});
});
}
resolveAll([promise1, promise2, promise3])
.then((res) => {
console.log(res, "result");
})
.catch((err) => console.log(err, "error"));
Actual result : [undefined, "success 2", "success 3"]
Expected result: ["success 1", "success 2", "success 3"]
https://codesandbox.io/s/fast-bush-6osdy?file=/src/index.js
Your problem is that
if (index === promiseArr.length - 1) {
resolve(arr);
}
just checks whether the last promise has resolved, but in your scenario the first promise is the last one that gets resolved because of the setTimeout.
One solution would be to keep a count of how many of the promises have resolved, e.g.
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("success 1"), 2000);
});
const promise2 = new Promise((resolve, reject) => {
resolve("success 2");
});
const promise3 = new Promise((resolve, reject) => {
resolve("success 3");
});
function resolveAll(promiseArr) {
let arr = [],
errorMsg = [],
resolvedPromiseCount = 0;
return new Promise((resolve, reject) => {
promiseArr.map((each_promise, index) => {
return each_promise.then((res) => {
console.log(res)
arr[index] = res;
resolvedPromiseCount++;
if (resolvedPromiseCount === promiseArr.length) {
resolve(arr);
}
})
.catch((err) => {
resolvedPromiseCount++;
errorMsg.push(err);
});
});
});
}
resolveAll([promise1, promise2, promise3]).then((res) => {
console.log(res, "result");
})
.catch((err) => console.log(err, "error"));
I have a use case, where I am doing an external API call from my code,
The response of the external API is required by my code further on
I am bumping into a scenario, where the external API call at times takes far too long to return a response,
casing my code to break, being a serverless function
So I want to set a time limit to the external API call,
Where if I don't get any response from it within 3 secs, I wish the code to gracefully stop the further process
Following is a pseudo-code of what I am trying to do, but couldn't figure out the logic -
let test = async () => {
let externalCallResponse = '';
await setTimeout(function(){
//this call sometimes takes for ever to respond, but I want to limit just 3secs to it
externalCallResponse = await externalCall();
}, 3000);
if(externalCallResponse != ''){
return true;
}
else{
return false;
}
}
test();
Reference -
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SSM.html#getParameters-property
I'm using AWS SSM's getParameters method
You cannot await setTimeout as it doesn't returns a Promise.
You could implement a function that returns a Promise which is fulfilled after 3 seconds.
function timeout(seconds) {
return new Promise((resolve) => {
setTimeout(resolve, seconds * 1000)
});
}
You can await the above function in your code passing the number of seconds you want to wait for
let test = async () => {
let externalCallResponse = '';
setTimeout(async () => {
externalCallResponse = await externalCall();
}, 0);
await timeout(3); // wait for 3 seconds
if(externalCallResponse != '') return true;
else return false;
}
Following code snippet demonstrates the usage of timeout function written above. It mocks a api request that returns a response after 4 seconds.
function timeout(seconds) {
return new Promise(resolve => {
setTimeout(resolve, seconds * 1000);
});
}
function apiRequest() {
return new Promise(resolve => {
setTimeout(() => resolve('Hello World'), 4000);
});
}
let test = async () => {
let externalCallResponse = '';
setTimeout(async () => {
externalCallResponse = await apiRequest();
}, 0);
await timeout(3); // wait for 3 seconds
if (externalCallResponse != '') return true;
else return false;
};
test()
.then(res => console.log(res))
.catch(err => console.log(err.message));
you can use something like this, I created a function that return a promise then I used this promise.
let test = async () => {
return promiseTimeout()
}
const promiseTimeout = () => {
return new Promise(async (resolve, reject) => {
setTimeout(function () {
let externalCallResponse=""
externalCallResponse = await externalCall();
if (externalCallResponse != '') {
return resolve(true);
}
else {
return resolve(false);
}
}, 3000);
})
}
test().then(result=>{
console.log(result);
});
You could do something like this:
const timeout = async (func, millis) => {
return new Promise(async (resolve, reject) => {
setTimeout(() => reject(), millis);
resolve(await func());
});
}
timeout(() => doStuff(), 3000)
.then(() => console.log('worked'))
.catch(() => console.log('timed out'));
Tests:
const timeout = async (func, millis) => {
return new Promise(async (resolve, reject) => {
setTimeout(() => reject(), millis);
resolve(await func());
});
}
const doStuffShort = async () => { // Runs for 1 second
return new Promise((resolve) => setTimeout(() => resolve(), 1000));
}
const doStuffLong = async () => { // Runs for 5 seconds
return new Promise((resolve) => setTimeout(() => resolve(), 5000));
}
timeout(() => doStuffShort(), 3000)
.then(() => console.log('1 worked'))
.catch(() => console.log('1 timed out'));
timeout(() => doStuffLong(), 3000)
.then(() => console.log('2 worked'))
.catch(() => console.log('2 timed out'));
I have some example code, but I can't figure out why the console is logging things out of order.
async function myAsyncFunction() {
setTimeout(() => { console.log("1st"); return "something"; }, 500);
}
async function mainline() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => { console.log("0th"); resolve(); }, 500);
});
promise.then(async () => {
return await myAsyncFunction();
})
.then((string) => {
console.log(string);
console.log("2nd");
});
}
mainline();
The console logs:
> 0th
> undefined
> 2nd
> 1st
So clearly my mainline isn't waiting to resolve the async function. What did I do wrong?
Your myAsyncFunction isn't returning anything. You need to wrap it in a promise.
function myAsyncFunction() {
return new Promise( (res, rej) =>
setTimeout(() => { console.log("1st"); res("something"); }, 500) );
}
function myAsyncFunction() {
return new Promise((resolve, reject) => {
setTimeout(() => { console.log("1st"); resolve("something"); }, 500);
});
}
async function mainline() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => { console.log("0th"); resolve(); }, 500);
});
promise.then(async () => {
return await myAsyncFunction();
})
.then((string) => {
console.log(string);
console.log("2nd");
});
}
mainline();
I am trying to cancel a promise as given below:
function example(cancel = Promise.reject()) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => resolve('jack-jack'), 5000);
cancel.then((res) => {
clearTimeout(timer);
reject('cancelled');
}, ()=>{})
});
}
var cancel=Promise.reject();
example(cancel).then((res) => console.log('res handled:' + res)).catch((err) => console.log('err handled:' + err));
console.log('attempting cancellation of promise');
cancel=Promise.resolve();
However I am not able to cancel it. What am I doing wrong over here?
In your code you pass already a complete (rejected) Promise to the function. And cancel=Promise.resolve(); after attempting cancellation of promise won't have any effect to promise that was passed to example because you just create a new resolved Promise.
If you want to cancel a running process then you might want choose such a solution:
function example(helper) {
return new Promise((resolve, reject) => {
helper.cancel = function() {
clearTimeout(timer)
reject('cancelled');
}
const timer = setTimeout(() => resolve('jack-jack'), 5000);
});
}
var helper = {};
example(helper).then((res) => console.log('res handled:' + res)).catch((err) => console.log('err handled:' + err));
console.log('attempting cancellation of promise');
helper.cancel()
You’ve assigned a rejected promise to a variable, passed the rejected promise into your function, and assigned a resolved promise to the variable. The two values the variable takes on are unrelated, and you can’t change the state of a settled promise.
Pass in a promise you can resolve:
let cancel;
let cancelPromise = new Promise((resolve) => {
cancel = resolve;
});
example(cancelPromise).then(…).catch(…);
console.log('attempting cancellation of promise');
cancel();
function example(cancel = Promise.reject()) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => resolve('jack-jack'), 5000);
cancel.then((res) => {
clearTimeout(timer);
reject('cancelled');
}, ()=>{})
});
}
let cancel;
let cancelPromise = new Promise((resolve) => {
cancel = resolve;
});
example(cancelPromise)
.then((res) => console.log('res handled:' + res))
.catch((err) => console.log('err handled:' + err));
console.log('attempting cancellation of promise');
cancel();
Since the cancel is set to reject, the then part
cancel.then((res) => { //you cannot use then for reject, as reject cannot be resolved.
clearTimeout(timer);
reject('cancelled');
}, ()=>{})
will never gets executed hence you have to resolve it, not reject it
function example(cancel = Promise.reject()) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => resolve('jack-jack'), 5000);
cancel.then((res) => { //you cannot use then for reject
clearTimeout(timer);
reject('cancelled');
}, ()=>{})
});
}
var cancel = Promise.resolve(); // just change to resolve
example(cancel).then((res) => console.log('res handled:' + res)).catch((err) => console.log('err handled:' + err));
console.log('attempting cancellation of promise');
cancel=Promise.resolve(); // this will have no effect as you have already passed a reject
Because you are passing rejected promise. Pass the resolved promise if you want cancel.then() block to run.
function example(cancel = Promise.resolve()) {
return new Promise((resolve, reject) => {
console.log(cancel);
const timer = setTimeout(() => {
resolve('jack-jack'), 5000
});
cancel.then((res) => {
console.log('CANCELLED');
clearTimeout(timer);
reject('cancelled');
}, () => {})
});
}
var cancel = Promise.resolve();
example(cancel).then((res) => console.log('res handled:' + res)).catch((err) => console.log('err handled:' + err));
console.log('attempting cancellation of promise');
cancel = Promise.resolve();