Use async in subscribe in Angular - javascript

I have a Subject, which used to be fire async chain.
Its sometimes looks like working correctly, sometimes looks like the subscribe doesn't wait for the resolve.
behavior.subscribe(async (result)=> {
await fnc1(result);
});
fnc1(in) {
return new Promise(async (resolve,reject) => {
for(let w of in.split(',')) {
await fnc2(w);
}
//if for finished, go to next subscribed value
resolve();
});
}
fnc2(w) {
return new Promise((resolve,reject) => {
let i = 0;
setInterval( () => {
i = i + 1;
if(i == 10) {
//go next value in for loop
resolve();
}
},100);
});
}

Related

async await issue promis

my function greetings works but when i try to return a promise to execute things once its done im getting nothing, what am i missing?
I have tried putting return resolve() as to make sure the function ends but still nothing, I can´t get the .then() to execute.
const greetingDivs = [firstDiv, secondDiv, thirdDiv, fourthDiv, fifthDiv]
let y = 0
function greetings() {
return new Promise((resolve, reject) => {
if (y == greetingDivs.length) {
// console.log('resolved')
resolve('done')
}
if (y != greetingDivs.length) {
setTimeout(() => {
let lastDiv = consoleOutput.appendChild(greetingDivs[y])
.scrollIntoView(false, { behavior: 'smooth' })
y++
greetings()
}, 300)
}
})
}
greetings().then(() => {
console.log('hello')
})
Your code only resolves one promise, while it creates 5. Notably, the first one, the one that greetings() returns, never resolves.
I would suggest promisifying setTimeout, so you have that logic once and for all. Then the looping can be done in an async function with a for loop and await:
const consoleOutput = document.body;
const [firstDiv, secondDiv, thirdDiv, fourthDiv, fifthDiv] = [
"Hello", "Welcome to this demo", "Hope it suits your case", "Test it", "and enjoy"].map(s => {
const div = document.createElement("div");
div.textContent = s;
return div;
});
const greetingDivs = [firstDiv, secondDiv, thirdDiv, fourthDiv, fifthDiv];
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
async function greetings() {
for (let div of greetingDivs) {
consoleOutput.appendChild(div)
.scrollIntoView(false, { behavior: 'smooth' });
await delay(300);
}
}
greetings().then(() => {
console.log('hello');
});
See ES6 promise for loop for more ideas on how you can create such loops.

How to write an asynchronous (promise-based) function that times out?

I want to have function which will do some work, if then is some condition true, then resolve promise. If not, wait little bit (lets say one second) and then try it again. Beside that if time limit expired, promise will reject. How can do I that? Look at my code example...If I wrap entire function in return new Promise(), I cant use await inside of it (which I need...I need to sleep some time if condition fail, and also I need wait to my await at end of my function).
Here is my example of code:
async funcWithTimeLimit(numberToDecrement, timeLimit){
let sleep = undefined;
let timeLimitTimeout = setTimeout(() => {
if (sleep)
clearTimeout(sleep);
return Promise.reject("Time limit of " + (timeLimit/1000) +" secs expired"); //reject
}, timeLimit);
while(numberToDecrement > 0){
for(let i = 10;(i > 0 && numberToDecrement > 0); i--){ //Do some work
numberToDecrement--;
}
if(numberToDecrement > 0){
await new Promise(resolve => sleep = setTimeout(resolve, 1000));
}
}
clearTimeout(timeLimitTimeout);
await new Promise((resolve, reject) => sleep = setTimeout(resolve, 500)); // Do something
return ""; //resolve
}
NOTE: My biggest problem is - how can I write this function to be able to catch rejection (at place where I call funcWithTimeLimit()) in case of time limit expiration?
There are two basic approaches. One is to repeatedly check the time and throw an exception once you hit the limit, the other is to race each awaited promise against a timeout. A third would require cooperation by the asynchronous tasks that you start, if they give you a way to cancel them in their interface, to simply offload the task of checking for timeout to them.
In neither of them you can reject the async function by throwing an exception from a setTimeout.
Rather simple:
function doSomething() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 500)); // Do something
}
}
async funcWithTimeLimit(numberToDecrement, timeLimit) {
while (numberToDecrement > 0) {
for (let i = 10; i > 0 && numberToDecrement > 0; i--) {
if (Date.now() > timelimit) throw new TimeoutError("Exceeded limit");
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
numberToDecrement--; // Do some work
}
if (numberToDecrement > 0) {
if (Date.now() + 1000 > timelimit) throw new TimeoutError("Exceeded limit");
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
// but ineffective here:
await doSomething();
return "";
}
Racing:
async funcWithTimeLimit(numberToDecrement, timeLimit) {
const timeout = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new TimeoutError("Exceeded limit"));
}, timeLimit - Date.now());
});
timeout.catch(e => void e); // avoid unhandled promise rejection if not needed
while (numberToDecrement > 0) {
for (let i = 10; i > 0 && numberToDecrement > 0; i--) {
// no guarding of synchronous loops
numberToDecrement--; // Do some work
}
if (numberToDecrement > 0) {
await Promise.race([ timeout,
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
new Promise(resolve => setTimeout(resolve, 1000)),
]);
}
}
await Promise.race([ timeout,
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
doSomething(),
]);
return "";
}
Notice that the setTimeout keeps the event loop open if when the function already completed, we just ignore the rejected promise. Better, but more laborious, is to cancel the timeout when we don't need it any longer:
async funcWithTimeLimit(numberToDecrement, timeLimit) {
let timer;
const timeout = new Promise((resolve, reject) => {
timer = setTimeout(() => {
reject(new TimeoutError("Exceeded limit"));
}, timeLimit - Date.now());
});
try {
// as before:
while (numberToDecrement > 0) {
for (let i = 10; i > 0 && numberToDecrement > 0; i--) {
numberToDecrement--; // Do some work
}
if (numberToDecrement > 0) {
await Promise.race([ timeout, new Promise(resolve => setTimeout(resolve, 1000)) ]);
}
}
await Promise.race([ timeout, doSomething() ]);
return "";
} finally {
clearTimeout(timer);
// ^^^^^^^^^^^^^^^^^^^^
}
}
With cooperation from the called function it's better of course:
function delay(t, limit) {
if (Date.now() + t > timelimit) throw new TimeoutError("Exceeded limit");
return new Promise(resolve => setTimeout(resolve, t));
}
function doSomething(limit) {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
xhr.cancel(); // custom API-dependent cancellation
reject(new TimeoutError("Exceeded limit"));
}, limit - Date.now());
const xhr = someApiCall(); // Do something
xhr.onerror = err => { clearTimeout(timeout); reject(err); };
xhr.onload = res => { clearTimeout(timeout); resolve(res); };
}
}
async funcWithTimeLimit(numberToDecrement, timeLimit) {
while (numberToDecrement > 0) {
for (let i = 10; i > 0 && numberToDecrement > 0; i--) {
numberToDecrement--; // Do some work
}
if (numberToDecrement > 0) {
await delay(1000, timeLimit);
// ^^^^^^^^^
}
}
await doSomething(timeLimit);
// ^^^^^^^^^
return "";
}
You can (where applicable) and should (where sensible) combine these approaches of course.

Why are the promises excute at the same time and not in order?

function page() {
return new Promise((resolve, reject) => {
setTimeout(function() {
fo = $("#root>div>div>main>div>div:nth-child(3)>div>div:nth-child(2)>div>div>div:nth-child(2)>div:nth-child(2)>div>div:nth-child(2)>div>div:nth-child(2)>div>ul>li:nth-last-child(1)")
fo.click()
console.log('翻页')
resolve();
}, 200)
})
}
function l() {
for (let i = 0, p = Promise.resolve(); i < 10; i++) {
p = p.then(() => {
return new Promise(resolve =>
setTimeout(function() {
console.log('wo')
$('#root>div>div>main>div>div:nth-child(3)>div>div:nth-child(2)>div>div>div:nth-child(2)>div:nth-child(2)>div>div:nth-child(2)>div>div:nth-child(1)>div:nth-child(1)>table>tbody>tr>td:nth-child(7)>div>div:nth-child(2)>a>span').eq(i).click()
resolve();
}, 200)
)
})
.then(() => {
return new Promise(resolve =>
setTimeout(function() {
console.log('wocao')
$('body>div:nth-last-child(1)>div>div>div:nth-child(3)>div:nth-child(2)>button>span').click()
resolve();
}, 200)
)
}
)
}
}
Promise.resolve().then(l).then(page)
Why is the program working not in order of the promises? How can I solve this problem? I've tried many times but they still execute together but not in order. Could somebody teach me? Very thanks.
Your function l is a unit function (i.e. returns nothing) and is therefore not awaitable as it does not return a Promise (either explicitly, or via being marked as async).
You should:
function l() {
let p = Promise.resolve() // this needs to exist out of the loop scope
for (let i = 0; i < 10; i++) {
p = p.then( /* ... */ )
}
return p; // <-- so it can be returned here
}
You need to resolve inside the timeout
new Promise(function(resolve, reject) {
// the function is executed automatically when the promise is constructed
// after 1 second signal that the job is done with the result "done"
setTimeout(() => {console.log("done");resolve()}, 1000);
}).then(function (res2, rej2) {
setTimeout(() => {console.log("done2")}, 1000);
})
but in your case you do not define parameters to the promise, hence they are default.

Javascript Promise chain not executing next then()

Here's my example code:
function recursiveFetch(num) {
// EXAMPLE that recursivley fetches all todos from example API
return new Promise(resolve => {
fetch("https://jsonplaceholder.typicode.com/todos/" + num)
.then((response) => {
return response.json();
})
.then((data) => {
if (num == 0) {
console.log("Done getting TODOs");
resolve(num);
} else {
recursiveFetch(num - 1);
}
});
});
}
new Promise(resolve => {
// Just using this for this example
resolve(10);
})
.then((num) => {
// This runs fine and returns a promise which is eventually resolved
return recursiveFetch(num);
})
.then((num) => {
// This never happens?
console.log("num is now " + num);
})
I can't tell why but the second .then is never run.
If I execute this code in the Firefox console I get the output Done getting TODOs but the "num is now " log is never called?
To fix your code as it is, you need
recursiveFetch(num - 1).then(resolve)
However, there are quite a few mistakes, how about this cleaned-up one:
async function fetchAll(num) {
let data = [];
for (let i = 1; i <= num; i++) {
let t = await fetch("https://jsonplaceholder.typicode.com/todos/" + i);
data.push(await t.json())
}
return data;
}
Promise
.resolve(3)
.then(fetchAll)
.then(console.log)
You need to add a resolve inside the "else" when you call recursively the function "recursiveFecth". Since the function is returning another promise you need to resolve then, otherwise it will exit inmediately.
I've tried it and it works:
function recursiveFetch(num) {
// EXAMPLE that recursivley fetches all todos from example API
return new Promise(resolve => {
fetch("https://jsonplaceholder.typicode.com/todos/" + num)
.then((response) => {
return response.json();
})
.then((data) => {
if (num == 0) {
console.log("Done getting TODOs");
resolve(num);
} else {
resolve(recursiveFetch(num - 1));
}
});
});
}
new Promise(resolve => {
// Just using this for this example
resolve(10);
})
.then((num) => {
// This runs fine and returns a promise which is eventually resolved
return recursiveFetch(num);
})
.then((num) => {
// This never happens?
console.log("num is now " + num);
})
You need to return the recursiveFetch in the inner promise
...
.then((data) => {
if (num == 0) {
console.log("Done getting TODOs");
resolve(num);
} else {
return recursiveFetch(num - 1);
}
});

Javascript: Add timeout after every request in Promise.all Map function

For the following function, I have to add a timeout after every GET request in array ajaxUrls. All the XHR GET request are in array ajaxUrls.
function getAllSearchResultProfiles(searchAjaxUrl) {
var ajaxUrls = [];
for (var i = 0; i < numResults; i += resultsPerPage) {
ajaxUrls.push(searchAjaxUrl + "&start=" + i);
}
return Promise.all(ajaxUrls.map(getSearchResultsForOnePage))
.then(function(responses) {
return responses.map(function(response) {
if (response.meta.total === 0) {
return [];
}
return response.result.searchResults.map(function(searchResult) {
return (searchResult);
});
});
})
.then(function(searchProfiles) {
return [].concat.apply([], searchProfiles);
})
.catch(function(responses) {
console.error('error ', responses);
});
}
function getSearchResultsForOnePage(url) {
return fetch(url, {
credentials: 'include'
})
.then(function(response) {
return response.json();
});
}
I want a certain timeout or delay after every GET request. I am facing difficulty in where exactly to add the timeout.
If you want to make requests in serial, you shouldn't use Promise.all, which initializes everything in parallel - better to use a reduce that awaits the previous iteration's resolution and awaits a promise-timeout. For example:
async function getAllSearchResultProfiles(searchAjaxUrl) {
const ajaxUrls = [];
for (let i = 0; i < numResults; i += resultsPerPage) {
ajaxUrls.push(searchAjaxUrl + "&start=" + i);
}
const responses = await ajaxUrls.reduce(async (lastPromise, url) => {
const accum = await lastPromise;
await new Promise(resolve => setTimeout(resolve, 1000));
const response = await getSearchResultsForOnePage(url);
return [...accum, response];
}, Promise.resolve([]));
// do stuff with responses
const searchProfiles = responses.map(response => (
response.meta.total === 0
? []
: response.result.searchResults
));
return [].concat(...searchProfiles);
}
Note that only asynchronous operations should be passed from one .then to another; synchronous code should not be chained with .then, just use variables and write the code out as normal.
I find a simple for loop in an async function to be the most readable, even if not necessarily the most succinct for things like this. As long as the function is an async function you can also create a nice pause() function that makes the code very easy to understand when you come back later.
I've simplified a bit, but this should give you a good idea:
function pause(time) {
// handy pause function to await
return new Promise(resolve => setTimeout(resolve, time))
}
async function getAllSearchResultProfiles(searchAjaxUrl) {
var ajaxUrls = [];
for (var i = 0; i < 5; i++) {
ajaxUrls.push(searchAjaxUrl + "&start=" + i);
}
let responses = []
for (url of ajaxUrls) {
// just loop though and await
console.log("sending request")
let response = await getSearchResultsForOnePage(url)
console.log("recieved: ", response)
responses.push(response)
await pause(1000) // wait one second
}
//responses.map() and other manilpulations etc...
return responses
}
function getSearchResultsForOnePage(url) {
//fake fetch
return Promise.resolve(url)
}
getAllSearchResultProfiles("Test")
.then(console.log)
If you want to add a delay in every request then add a setTimout() in your function which fetches data from api
function getSearchResultsForOnePage(url) {
return new Promise((resolve, reject) => {
fetch(url, {
credentials: 'include'
})
.then(response => reresponse.json())
.then(data => {
let timeout = 1000;
setTimeout(() => resolve(data), timeout);
});
}

Categories