Understanding the order of async calls and setTimeouts - javascript

The code below contains a function (logger) that sets a timeout, then consoles an async function, then consoles an element ('c').
The output here is 'a', 'b', 'c'.
How is it that 'c' waits for the other two to return? The await call is inside a console.log, so I thought the 'c' console will go ahead and execute since await isn't at the beginning of the console call for 'b'.
In other words, I thought the output should be 'c','a','b'.
Any enlightenment here would be much appreciated!
async function apiCall() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('b');
}, 50);
});
}
async function logger() {
setTimeout(() => console.log('a'), 10);
console.log(await apiCall());
console.log('c');
}
logger();

The await inside the console.log() call represents a point at which the logger() function will return an intermediate promise. Note that the line of code is equivalent to
let p = await apiCall();
console.log(p);
The expression parameters in a function call are evaluated before the function is called. Thus, the whole function (logger()) has to wait for the promise to resolve before it can proceed to call console.log().

Related

Despite Await the function is being executed instantly

I am new to Javascript and am facing trouble dealing with Async/Await. Here is my code, as per my understanding adding await puts on hold the function
Here, inside foo, it should put on hold the code at checker() and move to running the commands outside foo, but instead it executes the checker() function before console.log('the end').
function checker() {
console.log('inside checker');
return "Ok"
}
async function foo() {
let a = await checker(); // Shouldn't using await here let next line outside foo to execute
console.log("mid", a);
}
console.log(1);
foo();
console.log('the end');
// Output coming is:
//1
//inside checker
//the end
//mid Ok
Can someone please tell me what properties are making it to behave this way. I know there's something that I am missing but can't figure out what.
Your checker function is not asynchronous, but you also need to do something that is actually async within it.
At the moment, all of the promises are instantly resolved which provides no opportunity for later code to run.
We need to actually create a promise, merely defining a function as async isn't enough unless something inside is returning a promise, so we return a new promise which is resolved by the setTimeout callback.
Note that the timeout period is 0 which is enough to cause the javascript event loop to be invoked, which allows the next statement(s) after calling foo to be processed. Changing this to any positive integer will change the execution time but not the order of events.
function checker() {
return new Promise(resolve => {
setTimeout(() => resolve('Ok'), 0)
})
}
function foo() {
let a = checker();
console.log(new Date(), "mid");
return a
}
(async () => {
console.log(new Date(), 'start');
foo().then(v => console.log(new Date(), 'eventually', v));
console.log(new Date(), 'the end');
})()
You didn't define your function as async
function checker() {
console.log('inside checker');
return "Ok"
}
It should be
async function checker() {
console.log('inside checker');
return "Ok"
}
await tells your code to wait for the function to finish. If you want to continue running without waiting, then you shouldn't use the await keyword. async also needs to be on the function that you want to run asynchronously, in this case checker()
async function checker() {
console.log('inside checker');
return "Ok"
}
function foo() {
let a = checker();
console.log("mid", a);
}
console.log(1);
foo();
console.log('the end');
foo is an async function, ie the code inside it will wait if you use the await keyword
But the foo function returns a promise. so if you want to wait for foo either use
await foo();
or use
foo().then(() => { console.log('the end'); })

"A little help!" with the asynchronous syntax in javascript. I'm an old newbie, and I've finding this more than a little frustrating

I’d just like to run an async function, have it wait till it has a return value, pass that return value to another async function and wash and repeat a few times. I’ve seen some examples but I’m struggling to make them work in test code. Forgive all the baseball context, but I had to have a little fun with this self-inflicted nose bleed.
I can't even make it work without passing return values in the attached code, feeling pretty stupid at the moment...
whosOnFirst(1)
.then(whatsOnSecond(2))
.then(iDunnosOnthird(3))
I'd like to see the syntax to have whosOnFirst(1) execute, pass a return value to whatsOnSecond() and pass it's return value to iDunnosOnthird(). Creating the desired output (less the spaces) referenced in the bottom of the snippet.
I really appreciate the help!
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
return pause + 1; // delayed return value / adds 1 to input param
}, pause * 1000);
}
// Who's On First - run this first with an input param = 1
async function whosOnFirst(i) {
waitSec("who's on first", 1).then(result => {
return result;
})
}
// What's on Second - run after completion of whosOnFirst() using return for param
async function whatsOnSecond(i) {
waitSec("what's on second", i).then(result => {
return result;
})
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
async function iDunnosOnthird(i) {
waitSec("iDunno's on third", i).then(result => {
return result;
})
}
whosOnFirst(1)
.then(whatsOnSecond(2))
.then(iDunnosOnthird(3))
// CURRENT OUTPUT:
// who's on first called, waiting 1 second(s)...
// what's on second called, waiting 2 second(s)...
// iDunno's on third called, waiting 3 second(s)...
// who's on first sent 2 in a return, and done!
// what's on second sent 3 in a return, and done!
// iDunno's on third sent 4 in a return, and done!
// DESIRED OUTPUT:
// who's on first called, waiting 1 second(s)...
// who's on first sent 2 in a return, and done!
// what's on second called, waiting 2 second(s)...
// what's on second sent 3 in a return, and done!
// iDunno's on third called, waiting 3 second(s)...
// iDunno's on third sent 4 in a return, and done!
Working Solution
Your issue is that the waitSec is in a different context from the rest of the code. You need to lift it to the Promise context.
Then you have "promisified" code all the way down, and you can chain promises. Here it is working (further explanation below it):
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
return new Promise(resolve => {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
resolve(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
})
}
// Who's On First - run this first with an input param = 1
function whosOnFirst(i) {
return waitSec("who's on first", 1)
}
// What's on Second - run after completion of whosOnFirst() using return for param
function whatsOnSecond(i) {
return waitSec("what's on second", i)
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
function iDunnosOnthird(i) {
return waitSec("iDunno's on third", i)
}
whosOnFirst(1)
.then(whatsOnSecond)
.then(iDunnosOnthird)
.then(console.log)
Further Explanation
Your original implementation of waitSec always returns undefined to the caller:
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
return pause + 1; // delayed return value / adds 1 to input param
}, pause * 1000);
// return undefined happens here
}
The return inside the setTimeout does not return to your calling code, because your code never calls this function. It is invoked by the JS timer code, asynchronously.
Returning values from async code using a callback
The way to communicate a result out of there to a caller is to either take a callback as a parameter to the outer function and call that callback in the async function, like this:
function waitSec({callerName, pause, callback}) {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function () {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
callback(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
}
In which case you have the classic callback pattern of JS.
Using Promises, and why you might prefer them
Or, you return a Promise, and resolve it to return the value - as I demonstrated in the solution. For more about that, search for "how do I promisify a callback".
The benefit of doing it this way (with a Promise) is that Promises are composable, as demonstrated in the solution.
Promises are also await-able, so you can use async/await with Promise-returning functions.
You do not need to mark the Promise-returning functions as async.
If you mark a function as async, it returns a Promise, like this:
// A function marked async returns a Promise
async function simple() {
return 3
}
// Proof that it is a Promise
simple().then(console.log)
// The equivalent code explicitly returning a Promise - this is what `async` does
function simpleP(n = 0) {
return new Promise(resolve => resolve(n+1))
}
// Proof that it behaves the same
simpleP().then(console.log)
// Promise composition
simple()
.then(simpleP)
.then(console.log)
// Same functionality, using async/await
async function main() {
const num = await simple()
const res = await simpleP(num)
console.log(`${num} + 1 = ${res}`)
}
main()
Marking synchronous return functions async makes them composable with async code that actually needs a Promise to return a value. You can use it to lift your functions to the same async context and compose them.
// Synchronous function that is Promisified and composable
async function simple() {
return 3
}
To actually get a value out of an asynchronous function that takes a callback and provides its return via that, you must construct and return a Promise from your function, and then call the Promise resolve inside the callback that provides the value you want to communicate out.
function getDBRecordP(id) {
return new Promise((resolve, reject) =>
dbLib.get(id,
(err, result) => err ?
reject(err) :
resolve(result)))
}
In this case, you have to explicitly wire up the Promise machinery. This is pretty much the only time that you need to create and return a Promise.
What it is doing is wrapping a callback interface for you.
function getDBRecordP(id) {
return new Promise((resolve, reject) =>
dbLib.get(id,
(err, result) => err ?
reject(err) :
resolve(result)))
}
Notice in the callback example, you had to take a callback argument from the caller as a parameter, and then call that with the return value inside the callback that provides the value.
With the Promise machinery, you do take a callback argument, but it is not provided by the caller, it is provided by the Promise that you construct. And then you return the Promise to the caller.
The getDBRecordP function above is the essence of "how do I convert a function that takes a callback into a function that returns a Promise?".
The caller then passes in the callback to the the Promise.then() method.
So you have a machine that wraps the callback passing convention into a formal API that is composable, which callbacks are not.
The other aspect of the Promise machine is the .catch() method.
Whereas with callbacks, the convention has always been that a callback signature is (err, result), the Promise machine allows you to provide two callbacks - one for err and one for result.
Please utilize await keyword that is beautiful for async function. Your waitSec function should return a promise, also when you are resolving a promise, your value comes in .then((res)=> console.log(res)), use it for chaining like below:
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
return new Promise((resolve, reject) => {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function() {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
resolve(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
})
}
// Who's On First - run this first with an input param = 1
async function whosOnFirst(i) {
const resp = await waitSec("who's on first", 1);
return resp;
}
// What's on Second - run after completion of whosOnFirst() using return for param
async function whatsOnSecond(i) {
const resp = await waitSec("what's on second", i);
return resp;
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
async function iDunnosOnthird(i) {
const resp = await waitSec("iDunno's on third", i);
return resp;
}
whosOnFirst(1).then((firstResp) => {
whatsOnSecond(firstResp).then((secResp) => {
iDunnosOnthird(secResp).then((thirdResp) => {
console.log(thirdResp);
})
})
})
You can also use the values in chain like below with async/await:
// Arbitrary timer with input/output parameters for async understanding;
function waitSec(callerName, pause) { //
return new Promise((resolve, reject) => {
console.log(`${callerName} called, waiting ${pause} second(s)...`)
setTimeout(function() {
console.log(`${callerName} sent ${pause + 1} in a return, and done!`)
resolve(pause + 1); // delayed return value / adds 1 to input param
}, pause * 1000);
})
}
// Who's On First - run this first with an input param = 1
async function whosOnFirst(i) {
const resp = await waitSec("who's on first", 1);
return resp;
}
// What's on Second - run after completion of whosOnFirst() using return for param
async function whatsOnSecond(i) {
const resp = await waitSec("what's on second", i);
return resp;
}
// iDunno's on third - run after completion of whatsOnSecond(i) using return for param
async function iDunnosOnthird(i) {
const resp = await waitSec("iDunno's on third", i);
return resp;
}
let test = async() => {
var res1 = await whosOnFirst(1);
var res2 = await whatsOnSecond(res1);
var res3 = await iDunnosOnthird(res2);
console.log(res3);
}
test();
You need to await for each result like that:
async function main() {
const result1 = await whosOnFirst(1);
// whosOnSecond will not get called except after the whosOnFirst is done.
const result2 = await whosOnSecond(result1);
}
Here's how it's supposed to work:
function delay(milliseconds){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve();
}, milliseconds);
});
}
async function test(){
console.log('no delay, yet');
await delay(1000);
console.log('After 1 second');
await delay(1000);
console.log('2 seconds have passed');
await delay(3000);
console.log('Oh look, 5 seconds have passed!');
}
test();
async functions await until a Promise is resolved, then go to the next block of code after the Promise, possibly awaiting again... and again. I admit I thought it was strange that an async function really runs the awaits synchronously, but the async function itself it asynchronous... meaning another function executed immediately after an executed async function is likely to return results before the async function executes.
In regards to your example:
class Counter{
constructor(increment = 1, initially = 0){
this.increment = increment; this.count = initially;
}
inc(){
return this.count += this.increment;
}
dec(){
return this.count -= this.increment;
}
}
function delay(milliseconds){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve();
}, milliseconds);
});
}
const sec = new Counter;
function logCaller(callerName){
console.log(`${callerName} called, after ${sec.inc()} second(s)...`);
}
async function what(){
await delay(1000);
logCaller("who's on first");
await delay(1000);
logCaller("what's on second");
await delay(1000);
logCaller("iDunno's on third");
}
what();

What is the control flow through this code?

The following code (yes I know it is not idiomatic) prints 1,2. But I expected it to print 2,1.
(async()=>{
let resolve;
new Promise((r)=>{
resolve = r
}).then(()=>console.log('1'))
await resolve();
console.log('2')
})()
1. (async()=>{
2. let resolve;
3. new Promise((r)=>{
4. resolve = r
5. }).then(()=>console.log('1'))
6. await resolve();
7. console.log('2')
8. })()
Expected control flow:
Line 1: instantiate an anonymous async function expression
Line 8: ...and immediately call it
Line 2: variable declaration
Line 3 & 4: Instantiate Promise, run the executor function, assign the variable
Line 5: configure the `then` with the callback
Line 6: Schedule evaluation of the `then` for the next microtask
Line 6 (contd.): `resolve()` synchronously returns `undefined`
Line 6 (contd.): `await` triggered with undefined (unsure of action)
Line 7: Print `2`
Line 8: Pop execution contexts from stack
<on next microtask>
Line 5: Push execution context for anonymous function onto stack, print `1`
Why is this wrong? Does the async keyword schedule the expression to the right to be on the next microtask, keeping the order 1,2?
The issue seems to be the usage of await. When the keyword is encountered, the JavaScript interpreter will immediately pause the rest of the function and only resume the rest of the execution after the Promise is resolved. If you await nonPromiseValue it's treated as if you've created it via Promise.resolve() and is essentially equivalent to
Promise.resolve(nonPromiseValue)
.then((resolvedValue) => {
resolvedValue;
})
Or even more generally this:
await <await expression>;
<rest of body>;
return <return expression>;
Works like this:
Promise.resolve(<await expression>)
.then(resolvedValue => {
resolvedValue();
<rest of body>;
return <return expression>;
});
Thus and then you'd still have the rest of the body of the function put on the microtask queue and executed at least the next time the event loop picks a task.
NOTE: I'm omitting some details because the engine would be pausing and unpausing the execution, so if the await expression is part of another statement, you might get slightly different behaviour than what you'd expect at a glance, yet still the reason is immediate pausing when await is encountered. Thus expressions before and after an await keyword would be evaluated at different times and thus might have a different result.
At any rate, here is an example:
async function foo() {
console.log("foo - start");
"any value without await";
console.log("foo - end")
}
async function bar() {
console.log("bar - start");
await "any value";
console.log("bar - end")
}
console.log('start');
foo();
bar();
console.log('end');
For foo() the execution is straight forward:
Call the function.
Execute every line in the body.
Finish.
Yes, it's async but it's still executed synchronously. The only difference here is that the result would be a Promise but since it's all synchronous and there is no return keyword, then it's just implicitly Promise.resolve(undefined).
However, for bar() the execution is different:
Call the function.
Execute every line until you encounter await.
Turn the rest of the function to a Promise.
Pause and wait until the Promise is resolved.
Since we've await-ed a non-Promise this will happen immediately the next event loop iteration.
Continue the execution.
So, in fact the body of the function is wrapped in a Promise behind the scenes, so we actually run something similar to this:
async function bar() {
console.log("bar - start");
Promise.resolve("any value") //<-- await "any value";
.then((resolvedValue) => {
resolvedValue; //<-- nothing is done with it but it's what we awaited
console.log("bar - end"); //<-- the rest of the body of `bar()`
})
}
console.log('start');
bar();
console.log('end');
This is a simplified view but it's to just help visualise the fact that await will always halt execution and resume later. It uses the same Promise mechanics as usual and would delay the rest of the body for a later iteration of the event loop via a microtask but it's automatically handled for you.
Were we actually await-ing a real Promise, then you'd still get the equivalent behaviour:
async function bazAwait() {
console.log("bazAwait - start");
const result = await new Promise(resolve => resolve("some Promise value"));
console.log("bazAwait - end", result); //<-- the rest of the body of `bazAwait()`
}
async function bazPromiseEquivalent() {
console.log("bazPromiseEquivalent - start");
new Promise(resolve => resolve("some Promise value"))//<-- the awaited Promise
.then((promiseVal) => { //<-- the value the Promise resolves with
const result = promiseVal; //<-- the binding for the resolved value
console.log("bazPromiseEquivalent - end", result); //<-- the rest of the body of `bazPromiseEquivalent()`
});
}
console.log('start');
bazAwait();
bazPromiseEquivalent();
console.log('end');
I heavily suspect this is done in order to keep the behaviour the same regardless of whether or not await a Promise. Otherwise if you had a line like await myValue you'd get different execution depending on what myValue holds and it will not be obvious until you actually check that.
The following code appears to demonstrate how code after an await is treated as a microtask, similar to a then.
A tight indirectly-recursive loop of microtasks is started, that prints the first five integers to the console.
An async function is invoked synchronously. A string inside foo 2 is printed to the console after an await.
I included a generator function to remind us that they are synchronous.
async iterator included for completeness.
Note the interleaving with the "then tasks."
function printNums() {
let counter = 0
function go() {
console.log(counter++)
if(counter < 5) Promise.resolve().then(go)
}
Promise.resolve().then(go)
}
printNums()
async function foo() {
console.log('inside foo 1')
await 1
console.log('inside foo 2')
}
requestAnimationFrame(() => console.log('raf complete')) // ~16ms
setTimeout(() => console.log('macrotask complete')) // ~4ms
console.log('synch')
const syncIterable = {
*[Symbol.iterator]() {
console.log('starting sync iterable')
yield 'a'
yield 'b'
}
}
async function printSyncIterable() {
for(let y of syncIterable) {
console.log(y)
}
}
printSyncIterable()
foo().then(() => console.log('done'))
const asyncIterable = {
async *[Symbol.asyncIterator]() {
console.log('starting async iterable')
yield '⛱'
yield '🍿'
}
}
async function printAsyncIterable() {
for await(let z of asyncIterable) {
console.log(z)
}
}
printAsyncIterable()

JavaScript Async/Await console.log() on array returns empty

Hello Stack Overflow community,
I come to you with a problem related to JS async/await. I am trying to call an async function and then log the array to where the async function pushes the results to the console. If I call it like so directly in the console:
console.log(Page.data) - I can see that it has results in it, but if it is called on click of a button it logs an empty array.
// It is a nested object so do not worry if you don't exactly understand where Page.data comes from
Page.data = []
async function f1() {
// Fetch JSON data
// Process data
// Pushes at some point to the Page.data array
}
async function f2() {
// Fetch JSON data
// Process data
// Pushes at some point to the Page.data array
}
async function f3() {
// Fetch JSON data
// Process data
// Pushes at some point to the Page.data array
}
async function load(loader) {
let fn = async function() {};
if(condition1) fn = f1;
else if(condition2) fn = f2;
else fn = f3;
// This is the line that makes me problems
// According to documentation async functions return a promise
// So why would the array in the case be empty?
// Since I am telling it to display after the function is done
await fn(loader).then(console.log(Page.data))
}
This is just a template of my code and logic. I hope that you can understand where I am going.
Your help will be much appreciated.
The await expression causes async function execution to pause until a Promise is settled (that is, fulfilled or rejected), and to resume execution of the async function after fulfillment. When resumed, the value of the await expression is that of the fulfilled Promise.
For instance (this is an MDN example with some added comments):
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function f1() {
// note that here, you are "awaiting" the RESOLVED RESULT of the promise.
// there is no need to "then" it.
var x = await resolveAfter2Seconds(10);
// the promise has now already returned a rejection or the resolved value.
console.log(x); // 10
}
f1();
So you would "await" your function, which would hold up the execution until the promise either resolves or rejects. After that line, you would run your console.log, and it would log as expected. Short answer, "remove the then".
I should add, if the result of the "awaited" function is not a promise, it is converted to a promise (so technically, there is no need to return a promise, it'll wrap up your returned value for you).
the problem is that you can use then with await for the same method, lets check some examples provided by MDN:
this is a working Promise using async/await:
let myFirstPromise = new Promise((resolve, reject) => {
setTimeout(function() {
resolve("Success!");
}, 250)
})
const functionCaller = async() => {
const result = await myFirstPromise
console.log("the result: ", result);
}
functionCaller();
what you are trying is:
let myFirstPromise = new Promise((resolve, reject) => {
setTimeout(function() {
resolve("Success!");
}, 250)
})
const functionCaller = async() => {
// you are not returning anything here... actually you are not doing anything with the response. despite it shows something, it is not sending any response
await myFirstPromise.then(console.log("something happened"))
}
// then this function doesn't really get a value.
functionCaller();
so what you need to do in your load call, is to change it like this:
async function load(loader) {
let fn = async function() {};
if(condition1) fn = f1;
else if(condition2) fn = f2;
else fn = f3;
return await fn(loader)
}

Chain multiple AJAX calls with delay and passing variables

I'm working on a way to chain a set of 3 ajax calls per variable against an array of data with some delay in between.
Taking into account this answer.
I'm trying to update the code to achieve the following:
Add a delay in the b() and c() function
Pass a variable from a() to b() and from b() to c()
Is there a way I can pass a variable a() would generate to b() without using global variables? If not that's fine, I'd just like to know.
And how exactly can I get b() to be delayed for several seconds before processing c()? I would assume adding in a setTimeout would work as it's waiting for the promise before it starts c(), but this is not the case.
jsfiddle
function a(user, pass) {
return $.post('/echo/json/', {}, function(re){
console.log(1);
});
}
function b() {
setTimeout(function() {
return $.post('/echo/json/', {}, function(re){
console.log(2);
});
}, 3000);
}
function c() {
console.log(3);
}
var data = [{u: 'au', p: 'ap'}, {u: 'bu', p: 'bp'}];
var counter = 0;
function main() {
if(counter < data.length) {
$.when(a(data[counter].u, data[counter].p).then(b).then(c)).done(function(){
counter++;
main();
})
}
}
main();
To ensure that c() isn't executed until after the timeout code has been called, create your own promise and return it from the b() function. You should wrap the setTimeout function in this promise, and call the resolve(res) method to notify the .then() functions watching the promise, passing an object representing the data or body of the response.
function b(dataFromA) {
return new Promise((resolve, reject) => {
setTimeout(function() {
return $.post('/echo/json/', {}, res => {
let dataToB = res;
console.log(2);
resolve(dataToB);
});
}, 3000);
});
}
Note that b() now accepts data that can be passed from the response of the a() promise. You can manipulate this data inside the b(res) function or in the promiseFromA.then(res => { // ... Your promise callback code }); before calling b(res).
setTimeout as used in function b is not a promise. i will find some docs to site and post shortly but here is what i see.
to use a promise you need to call a promise method, setTimeout is not a promise. it is only a delay. it has to do with the event loop in javascript or in the javascript runtime to be more correct. there is some good stuff over here --> https://github.com/getify/You-Dont-Know-JS/tree/master/async%20%26%20performance
also .then only works to resolve a promise
getify really breaks it down but i would start with the javascript event loop

Categories

Resources