Can we get current promise in then? - javascript

I want to have some mechanism to redo then function in promise. Keep doing it until it pass some check
somePromise.then(() => {
if(state == "finish")
return;
// DoSomething async then repeat these codes
// could we do it by [return this;] ?
}).then(() => console.log("finish"));

In general, you don't get access to the current promise inside the .then() handler. And, for the most part, you couldn't do anything useful anyway. Adding another .then() handler does not do anything different than the .then() handler you're already in. Once fulfilled, promises never change their state.
If you want to repeat something, you make a function out of the thing you want to repeat and you call it over again from within the .then() handler and you return the resulting promise (thus chaining it to the previous promise).
Here's an example:
// utility delay function that returns a promise
function delay(t, val) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(val);
}, t);
});
}
// repeatedly call someAsyncOp() until we see the operation is finished
function repeatUntil(t) {
return someAsyncOp().then(function(state) {
if (state !== "finish") {
return delay(t).then(repeatUntil);
}
});
}
// sample usage
repeatUntil(5000).then(function() {
// state is finished here
}).catch(function(err) {
// error here
});

You can name the function used at .then(), recursively call same function at .then() of doSomethingAsync() call until state == "finish"
// name function used at `.then()`
somePromise.then(function re(data) {
if(state == "finish") {
return data;
};
// DoSomething async then repeat these codes
// call `re` at `.then()` chained to `doSomethingAsync()`
doSomethinAsync(data).then(re);
}).then((data) => console.log("finish", data));
See also multiple, sequential fetch() Promise

Related

Should I always return a promise when I have an async function?

When I write an async function it usually returns a promise:
export const myPromiseFunction = async (params) => {
// some logic
return Promise.resolve('resolved-value');
});
But I was wondering if it would be a mistake if this function would not return a promise, so for example:
export const myPromiseFunction = async (params) => {
// some logic
params.map(async (param) => {
await printParam(param);
async function printParam(par) {
// do some other stuff
Printer.print(par);
});
});
});
export class Printer {
public static async print(par) {console.log(par);} // I know it could not be async, but for the sake lets suppose it does
}
Is this a mistake / bad practice ? Or can we find a scenario when this will be valid and desirable ?
All async functions automatically return Promises. If you declare a function as async, it will return a Promise, even if your only return value is a simple value like a string or number. If you don't return explicitly, your async function will still return a Promise with a value of undefined.
In fact, it is more common for the body of an async function to return a simple value rather than a promise; the assumption is that your function is async because you await the promises you consume within it. As a consequence, even if you return the value 5, the return value is a Promise (that resolves to 5) representing the potential delay that comes from any await expressions in the function.
You don't have to return a Promise object explicitly in your async function, and it is redundant to do so if you're just wrapping a simple value like 'resolved-value'. Conversely, you can make a normal function behave like an async function if you always return a Promise (potentially with Promise.resolve) and you never synchronously throw an error within it.
async function myFunction() {
makeSomeCall(); // Happens immediately.
await someOtherPromise(); // myFunction returns a Promise
// before someOtherPromise resolves; if it
// does without error, the returned Promise
return 5; // resolves to 5.
}
/** Behaves the same as the above function. */
function myFunctionButNotAsync() {
try {
makeSomeCall();
// If you didn't have someOtherPromise() to wait for here, then
// this is where Promise.resolve(5) would be useful to return.
return someOtherPromise().then(() => 5);
} catch (e) {
return Promise.reject(e);
}
}
All that said, you may have occasion to explicitly return a Promise object (such as one produced by Promise.all or a separate Promise-returning function), which then observes rules similar to what Promise.resolve() observes: If the object you return from an async function is a Promise, or has a then function, then the automatic Promise the async function returns will wait for the specific Promise or Promise-like object you pass back with return.
async function myFunction() {
makeSomeCall(); // Happens immediately.
await anythingElse(); // You can still await other things.
return someOtherPromise(); // The promise myFunction returns will take
// the same outcome as the Promise that
// someOtherPromise() returns.
}
In a related sense, this is why return await is seen as redundant, though as described it does make a difference for the stack traces that you see if the wrapped promise is rejected.
Short answer: no, an async function doesn't have to returns a Promise. Actually, generally you wouldn't return a Promise object (unless you're chaining asynchronous events).
What async and await do is wait for a response from something that returns a Promise.
You first code example actually returns a resolved Promise. But what happens if the Promise isn't resolved properly ?
It's best to call a function that returns a Promise from another async function:
function getRequestResult() {
return new Promise(resolve => {
setTimeout(() => {
resolve('request sent');
}, 2000);
});
}
async function sendMyRequest() {
console.log('Sending request');
const result = await getRequestResult();
console.log(result);
// expected output: "resolved"
}
You can send rejections/errors within getRequestResult() that way, and also manage how these errors will be managed by the call in sendMyRequest() (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await).

When is async function promise resolved?

I have below code:
let func = () => {
return new Promise((resolve) => {
setTimeout(() => {
console.log("two");
resolve();
}, 3000);
})
};
let func2 = async () => {
console.log("one");
await func();
console.log("three");
}
(async () => {
await func2();
console.log("main"); // This should never be executed
})()
Noticed func2 never returns a value, the promise returned by func2 should never be fullfilled in my opinion. Hence console.log("main") should never be executed. However it is executed after console.log("three"). Can somebody explain this to me?
Noticed func2 never returns a value, the promise returned by func2 should never be fullfilled in my opinion.
That is not how async functions work. Your func2 returns when it is done executing. The return value of a function with no return statement is the specific value undefined. So, undefined becomes the resolved value of the promise. Remember, in Javascript, undefined is a specific value. It's as if you did return undefined at the end of your function block. So, since undefined is the return value, that becomes the resolved value of the async promise.
To fully cover all the bases, async functions always return a promise and that promise gets a resolved/rejected value one of these ways:
1. When you explicitly return a value from the function. That becomes the resolved value of the promise that the async function returns.
async function test() {
return "hello";
}
test().then(val => {
console.log(val); // "hello"
});
2. When you throw an exception. The exception becomes the reject reason of the promise that the async function returns.
async function test() {
throw new Error("ouch");
}
test().catch(err => {
console.log(err); // Shows error object with message "ouch"
});
3. When you return another promise. That promise is chained to the one that the async function returns and the promise that the async function returns will follow the one you returned (resolve when it resolves with the same value or reject with the same reason).
async function test() {
return new Promise(resolve => {
setTimeout(() => {
resolve("hi");
}, 1000);
});
}
test().then(val => {
console.log(val); // "hi"
});
4. When you return nothing. That is the same as in regular functions and it's equivalent to a return undefined at the end of the function block so the resolved value takes on the value of undefined.
So, this:
async function test() {
console.log("hi");
}
test().then(val => {
console.log(val); // undefined
});
Works exactly the same as this:
async function test() {
console.log("hi");
return undefined;
}
test().then(val => {
console.log(val); // undefined
});
A function that does not explexitly return something, actually returns undefined:
function test() { }
console.log(test());
Its the same with async functions, they also resolve to undefined when no other value was returned.
An async function doesn't need a return value in order to resolve. It is considered resolved when it finishes executing without an error.
If you throw new Error() inside of func2, console.log("main") will never be executed.
When you don't return from a function it implicitly returns a default value — normally undefined. So the promise returned by func2 will still resolve when the function returns.
From MDN:
A function without a return statement will return a default value. In
the case of a constructor called with the new keyword, the default
value is the value of its this parameter. For all other functions, the
default return value is undefined.
You can see this in your code if you alter it to this:
(async () => {
func2()
.then(d => console.log(d));
})()

Async function with both sync and async code

I have async function that returns true or false. It looks like following one:
class User {
async canManageGroup(group) {
if (typeof group === 'number') {
// group - id
group = await getGroupById(group)
} // else group is already loaded from DB
return this.id === group.manager.id
}
}
If group parameter is ID of group then function will make async call to DB, so function canManageGroup will execute asynchronously. But if group parameter is group model then function will only call return this.id === group.manager.id or it will execute syncronously. Is it good practice to write code in this way? Or I should transform synchronous code to asynchronous?
function makeAsync(cb) {
return new Promise(resolve => setImmediate(() => resolve(cb())))
}
class User {
async canManageGroup(group) {
if (typeof group === 'number') {
// group - id
group = await getGroupById(group)
} // else group is already loaded from DB
return await makeAsync(() => this.id === group.manager.id)
}
}
You can use the first example without problems.
When you use async, your function will return a Promise. If your code is sync, the status of the returned promise will be resolved and it is safe to run any promise-related code on it (then, catch, etc).
For example:
async function truePromise() {
return true;
}
truePromise().then(function(value){
console.log("Promise value:", value);
});
Just works :)
Should you do it?
Yes. It is okay and works okay thanks to the async keyword.
What you MUST NOT do is the following:
function dontDoIt(doSync) {
if (doSync) return false;
return Promise.resolve(false)
}
Why? Because:
if doSync is truhtly, it will return false (i.e a boolean)
if doSync is falsy, it will return a Promise that will resolve to false.
That is a HUGE difference.
Why?
Your function sometimes returns a promise, and other times return a boolean. It is inconsistent.
doSync(true) is a boolean, you can't use await nor .then.
doSync(false) is a Promise, you can use await and then.
Based on your comment to one of the answers you are concerned because of the answer to the question How do you create custom asynchronous functions in node.js?
The problem is, this function is inconsistent: sometimes it is asynchronous, sometimes it isn't. Suppose you have a consumer like this:
In js enviroments like nodejs is common practice that the callback that could be executed async, should always be called async, no matter if the actual code was really async.
So you are wondering if the following code will break that common practie.
async function doSomething() {
return true
}
doSomething()
.then(res => {
console.log(res)
})
This is not the case, because here you deal with Promises and a Promise is allowed to be resolve immediatly, the code above is basicly the same as if you would write:
Promise.resolve(true)
.then(res => {
console.log(res)
})
The async behaviour is ensured in the chaining part (then/catch), the callback you pass to the then/catch will be called async:
async function doSomething() {
return true
}
console.log('before');
doSomething()
.then(res => {
console.log('with in callback')
})
console.log('after');
As you can see the order of the logs is:
before
after
with in callback
So this is in the same order you would expect from an regular async callback function.
The first one is correct. There is no need to force anything to async if there doesn't need to be. Context switching isn't free, so if it doesn't have to wait for anything to complete, don't try and make it.

Returning values from Javascript Promise chain

Modern JS/Promise newbie here. I'm having trouble finding the answer to a simple question, despite the overwhelming amount of content on this subject.
I believe I have a decent understanding of JS Promises (thanks to various sources, including mdn and https://spin.atomicobject.com/2016/02/16/how-javascript-promises-work/)
I have consumed and produced Promises regularly in their simplest form, however I keep stumbling on methods in my inherited projects with the following pattern:
const doAsyncOp = () =>
$http.get(uri)
.then(
value => value,
reject => //...
);
My big question is: What happens when you simply return a value from your success handler? I need to consume this method, and need access to 'value' in the client code. Is this technically unhandled? Should I rewrite the implementation?
My big question is: What happens when you simply return a value from your success handler?
When you return a value from a .then() handler, that value becomes the resolved value of the parent promise chain: So, in this code:
// utility function that returns a promise that resolves after a delay
function delay(t, v) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, v), t);
});
}
function test() {
return delay(100, 5).then(val => {
// this return value becomes the resolved value of the promise chain
// that is returned from this function
return 10;
});
}
test().then(result => {
// logs 10
console.log(result);
});
When you run it, it will log 10 because of the return 10 in the .then() handler.
There are four possibilities from a .then() handler:
Return a regular value such as return 10 or return val. That value becomes the resolved value of the promise chain. If no value is returned (which in Javascript means the return value is undefined), then the resolved value of the promise chain is undefined.
Return a promise that ultimately resolves or is already resolved. This promise is added to the chain and the promise chain takes on the resolved value of that promise.
Return a promise that ultimately rejects or is already rejected. This promise is added to the chain and the promise chain takes on the rejected reason of the returned promise.
Throw an exception. If an exception is thrown inside the .then() handler, then the .then() infrastructure will catch it and turn the promise chain into a rejected state with the reject reason set to the value that is thrown. So, if you do throw new Error("User not found") inside a .then() handler, then that promise chain will be rejected with that error object as the reject reason.
In your specific code:
const doAsyncOp = () =>
$http.get(uri)
.then(
value => value,
reject => //...
);
There is no reason for the value => value. value is already the resolved value of the promise, you do not need to set it again.
And since the fat arrow function automatically returns any single statement, you're already returning the promise from $http.get().then() in your doAsyncOp() function. So, you can just do:
const doAsyncOp = () => $http.get(uri);
doAsyncOp().then(result => {
// handle the result here
}).catch(err => {
// handle error here
});
To get this to the client code, just return the promise from your function:
const doAsyncOp = () => $http.get(uri)
then in your client you can use:
doAsyncOp()
.then(value => {
// use value
}
.catch(err => { /* error */ }
I think you can use async and await for the resolved values.
function delay(t, v) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, v), t);
});
}
async function(){
await myData = delay(1000, 1000);
console.log(myData);
}

Javascript Promises returning strange object

(function() {
"use strict";
var storage = chrome.storage.sync;
var localStorage = null;
function getOutsideScope(property) {
if (localStorage.hasOwnProperty(property)) {
return localStorage[property];
}
}
function fulfill(data) {
return data;
}
function rejected(err) {
log(err);
}
window.app.storage = {
get: function(storageKey, storageProp) {
var promise = new Promise(function(fullfill, reject) {
chrome.storage.sync.get(storageKey, function(data) {
if (data.hasOwnProperty(storageKey)) {
fullfill(data[storageKey]);
} else {
reject(new Error(storageKey + " does not exist in the storage values."));
}
});
});
return promise.then(fulfill, rejected);
},
set: function(storageKey, storageItem) {
},
onChanged: function(fn) {
}
};
})();
So the above is my IIFE wrapper for chrome storage, and well the return is being a pain in the bleep. So I decided to give Promises a try, this is my first time so don't be too rough on me on this. Basically this is what I want to do
var property = app.storage.get("properties");
//property should equal "value"
//except it returns undefined
So adding the promises it does get the value except it returns this
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "value"}
Am I doing something wrong with Promises I've tried reading HTML5Rocks, the MDN, and video tutorials except it doesn't really mention much of how to return a value. This does NOT work
get:function(storageKey,storageProp) {
chrome.storage.sync.get(storageKey,function(data) {
//for simplicity and no error checking -_-
return data[storageKey];
});
}
The function returns exactly what it is supposed to return: A promise. To get the value once the promise is fulfilled, you add a callback via .then:
app.storage.get("properties").then(function(property) {
// property will be "value"
});
From MDN:
A Promise represents a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers to an asynchronous action's eventual success value or failure reason. This lets asynchronous methods return values like synchronous methods: instead of the final value, the asynchronous method returns a promise of having a value at some point in the future.
[...]
A pending promise can become either fulfilled with a value, or rejected with a reason (error). When either of these happens, the associated handlers queued up by a promise's then method are called.

Categories