I'm new to promises and trying to wrap my head around something that should be simple. Maybe someone can hit me in the head with it instead!
I've got these two functions:
//an arbitrary method that runs on delay to mimic an async process
method1 = function( _value, _callback ){
setTimeout(function(){
console.log('dependency1_resolved');
_callback.apply(this, [{valIs:_value}]);
}.bind(this), (Math.random() * 1000));
};
//something that can simple return the object
function returnVal(x){
console.log(x); //this logs 'Object {valIs: 4}'
return x;
}
due to it's async nature, I'd like to run this function in a promise to be used later (maybe even chain later) in my code.
here is my promise:
var promise = new Promise(
function(resolve, reject) {
var x = method1(4, returnVal);
resolve(x);
}
);
promise.then(function(val) {
console.log(val); // undefined
return val;
});
console.log(promise); //Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: undefined}
Does this have anything to do with the .apply method in the first function? What am I missing? Can someone please slap me?
Your returnVal callback doesn't actually do anything.
Instead, you need to actually pass a callback that accepts the value:
var promise = new Promise(
function(resolve, reject) {
method1(4, function(x) { resolve(x); });
}
);
You can also just pass resolve itself as the callback:
var promise = new Promise(
function(resolve, reject) {
method1(4, resolve);
}
);
Your method1 does return nothing, that's why x is undefined. That the returnVal callback does return something is insignificant, its return value is simply ignored by method1.
You will need to call resolve as the callback to make your code work:
new Promise(
function(resolve, reject) {
var x = method1(4, resolve);
}
).then(function(val) {
console.log(val); // undefined
return val;
});
See also How do I return the response from an asynchronous call?
Related
I read that async functions marked by the async keyword implicitly return a promise:
async function getVal(){
return await doSomethingAync();
}
var ret = getVal();
console.log(ret);
but that is not coherent...assuming doSomethingAsync() returns a promise, and the await keyword will return the value from the promise, not the promise itsef, then my getVal function should return that value, not an implicit promise.
So what exactly is the case? Do functions marked by the async keyword implicitly return promises or do we control what they return?
Perhaps if we don't explicitly return something, then they implicitly return a promise...?
To be more clear, there is a difference between the above and
function doSomethingAync(charlie) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(charlie || 'yikes');
}, 100);
})
}
async function getVal(){
var val = await doSomethingAync(); // val is not a promise
console.log(val); // logs 'yikes' or whatever
return val; // but this returns a promise
}
var ret = getVal();
console.log(ret); //logs a promise
In my synopsis the behavior is indeed inconsistent with traditional return statements. It appears that when you explicitly return a non-promise value from an async function, it will force wrap it in a promise.
I don't have a big problem with it, but it does defy normal JS.
The return value will always be a promise. If you don't explicitly return a promise, the value you return will automatically be wrapped in a promise.
async function increment(num) {
return num + 1;
}
// Even though you returned a number, the value is
// automatically wrapped in a promise, so we call
// `then` on it to access the returned value.
//
// Logs: 4
increment(3).then(num => console.log(num));
Same thing even if there's no return! (Promise { undefined } is returned)
async function increment(num) {}
Same thing even if there's an await.
function defer(callback) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(callback());
}, 1000);
});
}
async function incrementTwice(num) {
const numPlus1 = await defer(() => num + 1);
return numPlus1 + 1;
}
// Logs: 5
incrementTwice(3).then(num => console.log(num));
Promises auto-unwrap, so if you do return a promise for a value from within an async function, you will receive a promise for the value (not a promise for a promise for the value).
function defer(callback) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(callback());
}, 1000);
});
}
async function increment(num) {
// It doesn't matter whether you put an `await` here.
return defer(() => num + 1);
}
// Logs: 4
increment(3).then(num => console.log(num));
In my synopsis the behavior is indeed inconsistent with traditional
return statements. It appears that when you explicitly return a
non-promise value from an async function, it will force wrap it in a
promise. I don't have a big problem with it, but it does defy normal
JS.
ES6 has functions which don't return exactly the same value as the return. These functions are called generators.
function* foo() {
return 'test';
}
// Logs an object.
console.log(foo());
// Logs 'test'.
console.log(foo().next().value);
Yes, an async function will always return a promise.
According to the tc39 spec, an async function desugars to a generator which yields Promises.
Specifically:
async function <name>?<argumentlist><body>
Desugars to:
function <name>?<argumentlist>{ return spawn(function*() <body>, this); }
Where spawn "is a call to the following algorithm":
function spawn(genF, self) {
return new Promise(function(resolve, reject) {
var gen = genF.call(self);
function step(nextF) {
var next;
try {
next = nextF();
} catch(e) {
// finished with failure, reject the promise
reject(e);
return;
}
if(next.done) {
// finished with success, resolve the promise
resolve(next.value);
return;
}
// not finished, chain off the yielded promise and `step` again
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}
Your question is: If I create an async function should it return a promise or not? Answer: just do whatever you want and Javascript will fix it for you.
Suppose doSomethingAsync is a function that returns a promise. Then
async function getVal(){
return await doSomethingAsync();
}
is exactly the same as
async function getVal(){
return doSomethingAsync();
}
You probably are thinking "WTF, how can these be the same?" and you are right. The async will magically wrap a value with a Promise if necessary.
Even stranger, the doSomethingAsync can be written to sometimes return a promise and sometimes NOT return a promise. Still both functions are exactly the same, because the await is also magic. It will unwrap a Promise if necessary but it will have no effect on things that are not Promises.
Just add await before your function when you call it :
var ret = await getVal();
console.log(ret);
async doesn't return the promise, the await keyword awaits the resolution of the promise. async is an enhanced generator function and await works a bit like yield
I think the syntax (I am not 100% sure) is
async function* getVal() {...}
ES2016 generator functions work a bit like this. I have made a database handler based in top of tedious which you program like this
db.exec(function*(connection) {
if (params.passwd1 === '') {
let sql = 'UPDATE People SET UserName = #username WHERE ClinicianID = #clinicianid';
let request = connection.request(sql);
request.addParameter('username',db.TYPES.VarChar,params.username);
request.addParameter('clinicianid',db.TYPES.Int,uid);
yield connection.execSql();
} else {
if (!/^\S{4,}$/.test(params.passwd1)) {
response.end(JSON.stringify(
{status: false, passwd1: false,passwd2: true}
));
return;
}
let request = connection.request('SetPassword');
request.addParameter('userID',db.TYPES.Int,uid);
request.addParameter('username',db.TYPES.NVarChar,params.username);
request.addParameter('password',db.TYPES.VarChar,params.passwd1);
yield connection.callProcedure();
}
response.end(JSON.stringify({status: true}));
}).catch(err => {
logger('database',err.message);
response.end(JSON.stringify({status: false,passwd1: false,passwd2: false}));
});
Notice how I just program it like normal synchronous particularly at
yield connection.execSql and at yield connection.callProcedure
The db.exec function is a fairly typical Promise based generator
exec(generator) {
var self = this;
var it;
return new Promise((accept,reject) => {
var myConnection;
var onResult = lastPromiseResult => {
var obj = it.next(lastPromiseResult);
if (!obj.done) {
obj.value.then(onResult,reject);
} else {
if (myConnection) {
myConnection.release();
}
accept(obj.value);
}
};
self._connection().then(connection => {
myConnection = connection;
it = generator(connection); //This passes it into the generator
onResult(); //starts the generator
}).catch(error => {
reject(error);
});
});
}
I'm studying promises; can anybody explain me why this piece of code does not work is I call the
function add() inside the resolve?
<script>
async function f() {
function add() {
return 14+3;
}
let promise = new Promise((resolve, reject) => {
setTimeout(()=>{resolve(add)}, 3000); //this doesn't work
setTimeout(()=>{resolve(14+3)}, 3000); // this works
});
let result = await promise;
alert(result);
alert ("END");
}
f();
</script>
When you resolve with value you actually returning value from promise. In your example you resolve with function in arguments, so your promise returns function(function add)
So modify line,
resolve(add);
to
resolve(add());
I've been trying to understand how async/await works, all I want to do is have it wait until the value is returned. However, I could not get it to do so with callbacks nor promises nor async/await. What am I doing wrong, and why does async/await not work as I expect it to? (wait with running other code until this promise is resolved)
Many questions like this one link to "introduction pages". I've read them all, I understand what they do, I just don't know how to properly write them out, as I believe I did everything correctly, I'm just missing something
console.log("xcdcxcxcxccxvccvffdgfcd");
thing();
async function thing() {
let c = await otherthing();
console.log("dfasfadsfdasasdfa" + c)
}
async function otherthing() {
await setTimeout(function() {
return new Promise((resolve, reject) => {
let f = "rerewwqfewfasgsadf"
return resolve(f);
})
}, 3000);
}
console.log is supposed to wait until the promised value c is returned, however, it does not seem to work. Why?
Async/await works with functions that return promises. Your otherthing function doesn't return anything.
You can fix the code by returning the promise you are instantiating, like this:
thing();
async function thing() {
let c = await otherthing();
console.log("dfasfadsfdasasdfa" + c)
}
function otherthing() {
return new Promise((resolve, reject) => {
setTimeout(function () {
let f = "rerewwqfewfasgsadf"
resolve(f);
}, 3000)
});
}
You must return the new Promise from the otherthing function, not from the setTimeout callback. You are supposed to construct the promise, in its executor callback start the asynchronous action (setTimeout) and then asynchronously resolve() or reject() the promise.
function otherthing() {
return new Promise((resolve, reject) => {
setTimeout(function(){
let f = "rerewwqfewfasgsadf"
resolve(f);
}, 3000);
});
}
You don't need any async/await here for the function that is essentially only a promise wrapper around setTimeout. See also How do I convert an existing callback API to promises?.
You should move setTimeout inside Promise body and there do resolve(f)
function otherthing() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let f = "rerewwqfewfasgsadf"
resolve(f);
}, 3000);
});
}
async function otherthing has no return statement, so it will always return a promise that resolves to undefined.
await setTimeout(...) doesn't make any sense. You can only await a promise, and setTimeout does not return a promise.
You need to explicitly:
create a new Promise() inside otherthing
resolve() it with the desired value inside the callback function you pass to setTimeout
return the promise from otherthing
Don't create a promise inside the callback function you pass to setTimeout. It is pointless there.
A promise, just for example:
var P = new Promise(function (resolve, reject) {
var a = 5;
if (a) {
setTimeout(function(){
resolve(a);
}, 3000);
} else {
reject(a);
}
});
After we call the .then() method on the promise:
P.then(doWork('text'));
Then doWork function looks like this:
function doWork(data) {
return function(text) {
// sample function to console log
consoleToLog(data);
consoleToLog(b);
}
}
How can I avoid returning an inner function in doWork, to get access to data from the promise and text parameters? Are there any tricks to avoiding the inner function?
Perhaps the most straightforward answer is:
P.then(function(data) { return doWork('text', data); });
Or, since this is tagged ecmascript-6, using arrow functions:
P.then(data => doWork('text', data));
I find this most readable, and not too much to write.
You can use Function.prototype.bind to create a new function with a value passed to its first argument, like this
P.then(doWork.bind(null, 'text'))
and you can change doWork to,
function doWork(text, data) {
consoleToLog(data);
}
Now, text will be actually 'text' in doWork and data will be the value resolved by the Promise.
Note: Please make sure that you attach a rejection handler to your promise chain.
Working program: Live copy on Babel's REPL
function doWork(text, data) {
console.log(text + data + text);
}
new Promise(function (resolve, reject) {
var a = 5;
if (a) {
setTimeout(function () {
resolve(a);
}, 3000);
} else {
reject(a);
}
})
.then(doWork.bind(null, 'text'))
.catch(console.error);
Use currying.
var P = new Promise(function (resolve, reject) {
var a = 5;
if (a) {
setTimeout(function(){
resolve(a);
}, 3000);
} else {
reject(a);
}
});
var curriedDoWork = function(text) {
return function(data) {
console.log(data + text);
}
};
P.then(curriedDoWork('text'))
.catch(
//some error handling
);
The new answer to this question is to use arrow functions (which automatically bind the this and are much more readable). Google for links such as:
https://2ality.com/2016/02/arrow-functions-vs-bind.html
You can set the text like:
this.text = 'text';
P.then(data => doWork(data));
Note: this.text inside doWork will evaluate to 'text'.
This is suggested by jib above and that (or this!) should be the accepted answer now.
Lodash offers a nice alternative for this exact thing.
P.then(_.bind(doWork, 'myArgString', _));
//Say the promise was fulfilled with the string 'promiseResults'
function doWork(text, data) {
console.log(text + " foo " + data);
//myArgString foo promiseResults
}
Or, if you'd like your success function to have only one parameter (the fulfilled promise results), you can utilize it this way:
P.then(_.bind(doWork, {text: 'myArgString'}));
function doWork(data) {
console.log(data + " foo " + this.text);
//promiseResults foo myArgString
}
This will attach text: 'myArgString' to the this context within the function.
use this so you can access global variable inside the promise body
var ref=this;
Example
p.then((data)=>{
var ref=this;
});
I want to override the Promise constructor and the then method in Promise.
So, whenever someone creates a new Promise object, first my code will be executed and then the original promise constructor will get called.
Similarly, when someone calls .then function of Promise, first my code will get executed and then the callback of then function will get executed.
I tried this
var bind = Function.bind;
var unbind = bind.bind(bind);
function instantiate(constructor, args) {
return new (unbind(constructor, null).apply(null, args));
}
var oldProto = Promise.prototype;
Promise = function() {
console.log("Promise instantiated");
var promise = instantiate(Promise, arguments);
return promise;
};
Promise.prototype = oldProto;
While calling this using
var myFirstPromise = new Promise((resolve, reject) => {
setTimeout(function(){
resolve("Success!"); // Yay! Everything went well!
}, 250);
});
myFirstPromise.then((successMessage) => {
console.log("Yay! " + successMessage);
});
It led to infinite loop with console filled up with Promise instantiated log. I also tried the following:
Promise = function(Promise) {
MyPromise.prototype = Promise.prototype;
function MyPromise(){
console.log("Hello");
var promise = Function.prototype.bind.apply(MyPromise, arguments);
console.log(promise);
return promise;
}
}(Promise);
But, I am not sure whether the constructor overriden is the correct way to do it and how to define the then function for Promise.