I've just seen this code:
const buildSW = () => {
// This will return a Promise <- that comment was present on the code
workboxBuild
.injectManifest({
swSrc: 'src/sw.js'
// some code
})
.then(({ count, size, warnings }) => {
// some code
})
.catch(console.error);
};
buildSW();
The code seems to be working, has been in production like 1 year, but I get confused with the comment of This will return a Promise because I don't see that there is actually a return statement, and the whole code is inside { }.
Shouldn't be?:
return workboxBuild
.injectManifest({ ...etc
The code is part from this guides:
https://developers.google.com/web/tools/workbox/guides/generate-service-worker/workbox-build
where the return promise is present there. So how or why is working without return in the code showed above?
The comment is commenting on the line following it, and is redundant. E.g.
// a() will return a Promise
a().then(() => () // duh
...because it's equivalent to:
const promise = a();
promise.then(() => ()
Shouldn't be?: return workboxBuild.injectManifest({ ...etc
It depends on the contract you want buildSW to have.
As written, buildSW returns nothing and handles its own errors, using .catch(console.error) to emit them to console. Appropriate for event handlers — E.g. button.onclick = buildSW.
But if you expect to call buildSW from other places, a better contract is to return a promise, and leave error handling to the caller:
const buildSW = () => {
return workboxBuild.injectManifest({swSrc: 'src/sw.js'})
.then(({ count, size, warnings }) => {
// some code that may also fail
});
};
buildSW().catch(console.error);
I get confused with the comment because I don't see that there is actually a return statement, like it is present in the guide. Shouldn't it have one?
Yes, there should be a return statement. Sure, the function already catches errors and logs them, but it's still a good idea to always return a promise from asynchronous functions.
So how or why is working without return in the code showed above?
It's working only because the buildSW(); call you're doing doesn't try to use the promise that should be returned.
Related
Below is a minimized code snippet which causes the async warning in VS code (most recent version 1.70.). It is part of a load balancing algorithm which is to execute asynchronous tasks.
When the generateTask function returns a promise directly the warning disappears. When the Promise.all promise is returned I get the warning "await has no effect" (w1). Despite the warning the code behaves as wanted and waits until tasks.reduce is ready with the tasks array (which has just one task here of course for testing).
The function of the code is ok. So I am wondering whether that is a problem of the Javascript parser of VS Code. When I put the same code snippet into the script part of an HTML file VS Code does not produce the warning. As well if I put it to a ts Typescript file.
Any idea to that would be appreciated
// nodejs 16 test function. VSCode 1.70.1 IDE
async function jobExecutorMinified() {
const generateTask = () => {
return () => {
const p = new Promise(resolve => {
setTimeout(() => {
resolve();
}, 5000);
});
// returning the promise of Promise.all -> warning w1
return Promise.all([p]);
// returning the promise directly -> no warnings
// return p;
}
}
const tasks = [generateTask()];
/*w1*/await tasks.reduce((prevTask, currTask) => {
return prevTask.then(() => {
return currTask();
});
}, Promise.resolve());
console.log('task chunking is ready');
}
jobExecutorMinified().then(() => {
console.log('job executor minified returned');
});
As discussed in the comments, this is just a false positive of the linter, which is not able to determine what reduce() returns (or probably: sees that Array.prototype.reduce is not an async function). Either give it some hints (type annotations, e.g. via jsdoc) or just ignore it.
However, you really shouldn't use reduce here. If you're using async/await anyway, just write a normal loop instead:
for (const currTask of tasks) {
await currTask();
}
I am trying to test a console.error output when a promise rejects using Jest. I am finding that the promise seems to be resolving after my test has run, causing the test to fail.
Example Function:
export default function doSomething({ getData }) {
const success = data => {
//do stuff with the data
}
const handleError = () => {
//handle the error
}
getData.then(response => success(response)).catch(error => {
console.error(error)
handleError()
})
}
Example Test File:
import doSomething from "doSomething"
it("should log console.error if the promise is rejected", async () => {
const getData = new Promise((resolve, reject) => {
reject("fail");
});
global.console.error = jest.fn();
await doSomething({ getData });
expect(global.console.error).toHaveBeenCalledWith("fail");
})
//fails with global.console.error has not been called
When I was exploring the problem, I noticed that if I add in a console.log and await that, it works.
This will pass...
import doSomething from "doSomething"
it("should log console.error if the promise is rejected", async () => {
const getData = new Promise((resolve, reject) => {
reject("fail");
});
global.console.error = jest.fn();
await doSomething({ getData });
await console.log("anything here");
expect(global.console.error).toHaveBeenCalledWith("fail");
})
How do I test for this correctly? Should I refactor how my getData function is being called? It needs to get called as soon as the doSomething function is called.
Why is the original test failing?
The trick for understanding why the first test example won't pass is in digging into what the await operator is actually doing. From the Mozilla docs:
[rv] = await expression;
expression - A Promise or any value to wait for.
rv - Returns the fulfilled value of the promise, or the value itself if it's not a Promise.
In your first test the value of expression is the return value of the doSomething function. You aren't returning anything from this function, so the return value will be undefined. This isn't a Promise, so nothing for await to do, it will just return undefined and move on. The expect statement will then fail, as you haven't actually awaited the inner promise: getData.then(...).catch(...).
To fix the test, without adding in the extra line, await console.log("anything here");, simply return the inner promise from the doSomething function, so that the await operator will actually operate on the Promise.
export default function doSomething({ getData }) {
return getData.then(...).catch(...);
...
}
Is this the right way to test this?
I don't think that there is anything majorly wrong with how the doSomething function is written. This kind of dependency injecting usually makes functions easier to test than trying to mock out the inner workings of a function.
I would only recognise though that because you are injecting a Promise (getData), and resolving it within the function, you have made the doSomething function asynchronous (which is what made it more complicated to test).
Had you resolved the Promise and then called doSomething on the value it resolves to, getData.then(doSomething).catch(handleError), your doSomething function would have been synchronous and easier to test. I would also say that writing it this way makes it much more verbose that something asynchronous is going on, whereas the original, doSomething({ getData }), hides that within the doSomething function body.
So nothing strictly incorrect, but maybe a few things to think about that might make testing easier and code more verbose. I hope that helps!
In my script I need to make a XMLrequest, get the response from the server (if resolved), parse it and place the parsed result in a variable.
Only later, with this variable I can do lot of stuff.
So basically, I am trying to separate the creation of such parser object from the rest of my code, in order to:
place the above process into a function in an external script which I can call whenever I need.
not having all my code within a .then() method
organize my code into logical files each devoted to a specific task
So I came up with this piece of code (just a sample) to explain my needs.
As I am fairly new to asyncronous programming, would it be fine to do like so, given that the response should be very fast to resovle (or to reject) in my case?
If it's ok, I would then put this inside a separate file to be able to import it and call testing() (or whatever the name will be) from anywhere I need.
function delay(input) {
return new Promise(function(resolve, reject) {
// some async operation here
setTimeout(function() {
// resolve the promise with some value
resolve(input + 10);
}, 500);
});
}
function testing() {delay(5).then(result => {return document.write(result)})};
testing();
EDIT
Ok, so I think I hit the problem thanks to the answer of #DrewReese and the links in the comments.
I am trying to solve this situation: the code above was misleading to understand my point, but I guess tere's no really simple solution.
Look at this code (it's basically the same as the one above, except fot the last three lines):
function delay(input) {
return new Promise(function(resolve, reject) {
// some async operation here
setTimeout(function() {
// resolve the promise with some value
resolve(input + 10);
}, 500);
});
}
function testing() {delay(5).then(result => {return result})};
var test = testing();
document.write(test);
So in this case, I know when I am defining test the output is 'undefined' because the Promise in testing() has not yet resolved.
The problem I'm trying to solve is: is there (if any) a way to define test only when the Promise is resolved without wrapping it in a then() and maybe outputting something different when it's not resolved (like "LOADING...").
I don't know basically if it is possible to check if a variable has a Promise pending and outputting two different values: when it's waiting and when it's resolved/rejected.
Hopefully I was clear enough, otherwise I'll test more and come back with another question if needed.
The whole point of promises (promise chains) is to escape from "nesting hell" by flattening your "chain", so yes, when your promise chain is resolving blocks of code, it is returning a promise which is then-able. I hope this helps illustrate:
someAsyncHttpRequest(parameter) // <- Returns Promise
.then(result => {
// do something with result data, i.e. extract response data,
// mutate it, save it, do something else based on value
...
// You can even return a Promise
return Promise.resolve(mutatedData);
})
.then(newData => { // <- mutadedData passed as parameter
// do new stuff with new data, etc... even reject
let rejectData = processNewData(newData);
return Promise.reject(rejectData);
})
.catch(err => {
console.log('Any caught promise rejection no matter where it came from:', err);
})
.finally(() => {// You can even run this code no matter what});
If you need to use any variable values you set within the chain, then you need to make the outer function async and await on the resolution of the promise chain:
asyncFunction = async (parameter) => {
let asyncValue;
await someAsyncHttpRequest(parameter)
.then(result => {
...
asyncValue = someValue;
...
});
// safe to use asyncValue now
};
So for you:
function delay(input) {
return new Promise(function(resolve, reject) {
// some async operation here
setTimeout(function() {
// resolve the promise with some value
resolve(input + 10);
}, 500);
});
}
**async** function testing() { // Declare this an asynchronous function
let value = **await** delay(5); // Now you can await the resolution of the Promise
console.log(value); // Outputs resolved value 15!
return value; // Just returns resolved Promise
}
var test = testing();
console.log(test);
/** Outputs the Promise!
Promise {<pending>}
__proto__:Promise
[[PromiseStatus]]:"resolved"
[[PromiseValue]]:15
*/
test.then(console.log)); // Outputs returned resolved value, 15
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I have an es6 class, with an init() method responsible for fetching data, transforming it, then update the class's property this.data with newly transformed data.
So far so good.
The class itself has another getPostById() method, to just do what it sounds like. Here is the code for the class:
class Posts {
constructor(url) {
this.ready = false
this.data = {}
this.url = url
}
async init() {
try {
let res = await fetch( this.url )
if (res.ok) {
let data = await res.json()
// Do bunch of transformation stuff here
this.data = data
this.ready = true
return data
}
}
catch (e) {
console.log(e)
}
}
getPostById(id){
return this.data.find( p => p.id === id )
}
}
Straightforward, except I have an async/await mechanism in the init() method.
Now, this code will work correctly:
let allPosts = new Posts('https://jsonplaceholder.typicode.com/posts')
allPosts.init()
.then( d => console.log(allPosts.getPostById(4)) )
// resulting Object correctly logged in console
but it only gets printed into the console:
How could I use allPosts.getPostById(4) as a return of a function ?
Like:
let myFunc = async () => {
const postId = 4
await allPosts.init() // I need to wait for this to finish before returning
// This is logging correct value
console.log( 'logging: ' + JSON.stringify(allPosts.getPostById( postId ), null, 4) )
// How can I return the RESULT of allPosts.getPostById( postId ) ???
return allPosts.getPostById( postId )
}
myFunc() returns a Promise but not the final value. I have read several related posts on the subject but they all give example of logging, never returning.
Here is a fiddle that includes two ways of handling init(): using Promise and using async/await. No matter what I try, I can't manage to USE the FINAL VALUE of getPostById(id).
The question of this post is: how can I create a function that will RETURN the VALUE of getPostById(id) ?
EDIT:
A lot of good answers trying to explain what Promises are in regards to the main execution loop.
After a lot of videos and other good reads, here is what I understand now:
my function init() correctly returns. However, within the main event loop: it returns a Promise, then it is my job to catch the result of this Promise from within a kinda parallel loop (not a new real thread). In order to catch the result from the parallel loop there are two ways:
use .then( value => doSomethingWithMy(value) )
use let value = await myAsyncFn(). Now here is the foolish hiccup:
await can only be used within an async function :p
thus itself returning a Promise, usable with await which should be embed in an async function, which will be usable with await etc...
This means we cannot really WAIT for a Promise: instead we should catch parallel loop indefinitely: using .then() or async/await.
Thanks for the help !
As for your comment; I'll add it as answer.
The code you write in JavaScript is run on one thread, that means that if your code could actually wait for something it will block any of your other code from getting executed. The event loop of JavaScript is explained very well in this video and if you like to read in this page.
A good example of blocking code in the browser is alert("cannot do anything until you click ok");. Alert blocks everything, the user can't even scroll or click on anything in the page and your code also blocks from executing.
Promise.resolve(22)
.then(x=>alert("blocking")||"Hello World")
.then(
x=>console.log(
"does not resolve untill you click ok on the alert:",
x
)
);
Run that in a console and you see what I mean by blocking.
This creates a problem when you want to do something that takes time. In other frameworks you'd use a thread or processes but there is no such thing in JavaScript (technically there is with web worker and fork in node but that's another story and usually far more complicated than using async api's).
So when you want to make a http request you can use fetch but fetch takes some time to finish and your function should not block (has to return something as fast as possible). This is why fetch returns a promise.
Note that fetch is implemented by browser/node and does run in another thread, only code you write runs in one thread so starting a lot of promises that only run code you write will not speed up anything but calling native async api's in parallel will.
Before promises async code used callbacks or would return an observable object (like XmlHttpRequest) but let's cover promises since you can convert the more traditional code to a promise anyway.
A promise is an object that has a then function (and a bunch of stuff that is sugar for then but does the same), this function takes 2 parameters.
Resolve handler: A function that will be called by the promise when the promise resolves (has no errors and is finished). The function will be passed one argument with the resolve value (for http requests this usually is the response).
Reject handler: A function that will be called by the promise when the promise rejects (has an error). This function will be passed one argument, this is usually the error or reason for rejection (can be a string, number or anything).
Converting callback to promise.
The traditional api's (especially nodejs api's) use callbacks:
traditionalApi(
arg
,function callback(err,value){
err ? handleFail(err) : processValue(value);
}
);
This makes it difficult for the programmer to catch errors or handle the return value in a linear way (from top to bottom). It gets even more impossible to try and do things parallel or throttled parallel with error handling (impossible to read).
You can convert traditional api's to promises with new Promise
const apiAsPromise = arg =>
new Promise(
(resolve,reject)=>
traditionalApi(
arg,
(err,val) => (err) ? reject(err) : resolve(val)
)
)
async await
This is what's called syntax sugar for promises. It makes promise consuming functions look more traditional and easier to read. That is if you like to write traditional code, I would argue that composing small functions is much easier to read. For example, can you guess what this does?:
const handleSearch = search =>
compose([
showLoading,
makeSearchRequest,
processRespose,
hideLoading
])(search)
.then(
undefined,//don't care about the resolve
compose([
showError,
hideLoading
])
);
Anayway; enough ranting. The important part is to understand that async await doesn't actually start another thread, async functions always return a promise and await doesn't actually block or wait. It's syntax sugar for someFn().then(result=>...,error=>...) and looks like:
async someMethod = () =>
//syntax sugar for:
//return someFn().then(result=>...,error=>...)
try{
const result = await someFn();
...
}catch(error){
...
}
}
The examples allways show try catch but you don't need to do that, for example:
var alwaysReject = async () => { throw "Always returns rejected promise"; };
alwaysReject()
.then(
x=>console.log("never happens, doesn't resolve")
,err=>console.warn("got rejected:",err)
);
Any error thrown or await returning a rejected promise will cause the async function to return a rejected promise (unless you try and catch it). Many times it is desirable to just let it fail and have the caller handle errors.
Catching errors could be needed when you want the promise to succeed with a special value for rejected promises so you can handle it later but the promise does not technically reject so will always resolve.
An example is Promise.all, this takes an array of promises and returns a new promise that resolves to an array of resolved values or reject when any one of them rejects. You may just want to get the results of all promises back and filter out the rejected ones:
const Fail = function(details){this.details=details;},
isFail = item => (item && item.constructor)===Fail;
Promise.all(
urls.map(//map array of urls to array of promises that don't reject
url =>
fetch(url)
.then(
undefined,//do not handle resolve yet
//when you handle the reject this ".then" will return
// a promise that RESOLVES to the value returned below (new Fail([url,err]))
err=>new Fail([url,err])
)
)
)
.then(
responses => {
console.log("failed requests:");
console.log(
responses.filter(//only Fail type
isFail
)
);
console.log("resolved requests:");
console.log(
responses.filter(//anything not Fail type
response=>!isFail(response)
)
);
}
);
Your question and the comments suggest you could use a little intuition nudge about the way the event loop works. It really is confusing at first, but after a while it becomes second nature.
Rather than thinking about the FINAL VALUE, think about the fact that you have a single thread and you can't stop it — so you want the FUTURE VALUE -- the value on the next or some future event loop. Everything you write that is not asynchronous is going to happen almost immediately — functions return with some value or undefined immediately. There's nothing you can do about. When you need something asynchronously, you need to setup a system that is ready to deal with the async values when they return sometime in the future. This is what events, callbacks, promises (and async/await) all try to help with. If some data is asynchronous, you simply can not use it in the same event loop.
So what do you do?
If you want a pattern where you create an instance, call init() and then some function that further process it, you simply need to setup a system that does the processing when the data arrives. There are a lot of ways to do this. Here's one way that's a variation on your class:
function someAsync() {
console.log("someAsync called")
return new Promise(resolve => {
setTimeout(() => resolve(Math.random()), 1000)
})
}
class Posts {
constructor(url) {
this.ready = false
this.data = "uninitilized"
this.url = url
}
init() {
this.data = someAsync()
}
time100() {
// it's important to return the promise here
return this.data.then(d => d * 100)
}
}
let p = new Posts()
p.init()
processData(p)
// called twice to illustrate point
processData(p)
async function processData(posts) {
let p = await posts.time100()
console.log("randomin * 100:", p)
}
init() saves the promise returned from someAsync(). someAsync() could be anything that returns a promise. It saves the promise in an instance property. Now you can call then() or use async/await to get the value. It will either immediately return the value if the promise has already resolved or it will deal with it when it has resolved. I called processData(p) twice just to illustrate that it doesn't calle the someAsync() twice.
That's just one pattern. There are a lot more — using events, observables, just using then() directly, or even callbacks which are unfashionable, but still can be useful.
NOTE: Wherever you use await it has to be inside an async function.
Check out the UPDATED FIDDLE
You need to use await myFunc() to get the value you expect from getPostById because an async function always returns a promise.
This sometimes is very frustrating as the whole chain needs to be converted into async functions but that's the price you pay for converting it to a synchronous code, I guess. I am not sure if that can be avoided but am interested in hearing from people who have more experience on this.
Try out the below code in your console by copying over the functions and then accessing final and await final.
NOTE:
An async function CAN contain an await expression.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
There is no rule that is must have await in order to even declare an async function.
The example below uses an async function without await just to show that an async function always returns a promise.
const sample = async () => {
return 100;
}
// sample() WILL RETURN A PROMISE AND NOT 100
// await sample() WILL RETURN 100
const init = async (num) => {
return new Promise((resolve, reject) => {
resolve(num);
});
}
const myFunc = async (num) => {
const k = await init(num);
return k;
}
// const final = myFunc();
// final; This returns a promise
// await final; This returns the number you provided to myFunc
I have two requests and some function to be called in between. The flow is that when the first promise is called and finished, no matter what result is (success or failure) some non-promise related function should be called and only after we should call the second promise. So this is how I ended up doing it, which does not look like a good solution.
funtionReturnsPromise()
.then(()=>{})
.catch(()=>{})
.then(()=>{
nonPromiseRelatedFuntion()
})
.then(()=>{
return funtionReturnsPromise2()
})
Since the desired flow is:
Promise > Function > Promise
On which the function is executed no matter the outcome of the first promise, you can simply do something like:
function secondFunction(outcome) {
// do stuff
return funtionReturnsPromise2()
}
functionReturnsPromise().then(secondFunction).catch(secondFunction)
Now, on another topic, I would not call the second function 'unrelated' since it clearly, according to your explanation, needs to be called after the first promise is fulfilled.
Assuming nonPromiseRelatedFuntion is sync and you're not interested in return value of functionReturnsPromise
functionReturnsPromise()
.then(
// no matter what happens, the function is invoked
() => { nonPromiseRelatedFunction() },
error => {
nonPromiseRelatedFunction()
// do error handling from functionReturnsPromise
}
)
.then(() => functionReturnsPromise2() }
.catch(console.error)
If you need a value:
functionReturnsPromise()
.then(
value => {
nonPromiseRelatedFunction()
return functionReturnsPromise2(value)
},
error => {
nonPromiseRelatedFunction()
// do error handling from functionReturnsPromise
}
)
.catch(console.error) // error handling from functionReturnsPromise2
Like the other answers I'm assuming
nonPromiseRelatedFunction is synchronous
you really don't care about any return values
nonPromiseRelatedFunction and funtionReturnsPromise2 should be executed in all circumstances
Having read ALL the comments to the question, I see the above is not an assumption after all
The simplest solution is to get rid of the first .then
funtionReturnsPromise()
.catch(()=>{})
.then(()=>{
nonPromiseRelatedFuntion()
})
.then(()=>{
return funtionReturnsPromise2()
})
Note: such code could be written
funtionReturnsPromise()
.catch(()=>{})
.then(nonPromiseRelatedFuntion)
.then(funtionReturnsPromise2)
sure, the last two functions will receive arguments, but if the code in those functions ignores arguments anyway, then their will be no issue