I have the following code:
function doSomething() {
//xhr here
setTimeout(function() {
var value = 42;
}, 10);
return {
then: function(callback) {
callback(value);
}
};
}
doSomething().then(function(result) {
log("got a result", result);
});
And can't figure out how to access the value.
I need this to be promise-based solution in order to use in multiple places
JSFidle link
Update:
We are not using any libraries in that projects
There are a couple of problems there:
value is local to the function you're passing into setTimeout, because that's where you've declared it. You could fix this issue by declaring it in doSomething instead.
The bigger issue is that what you have there isn't a promise, it's just a function that returns an object when you call it that has a then method. Here's the order in which things happen:
You call doSomething
It sets a timer to set a value.
It creates an object with a then function.
It returns the object.
You call the then function immediately.
then tries to access value (which it can't because of the declaration issue, but would be a problem anyway).
Some time later, value is set by the callback when the timer fires.
To be a promise, the then function on object you return would have to store a reference to the callback passed into it, and call the callback later, when value has been set (e.g., the promise has been fulfilled).
Rather than implementing your own promises library, I'd suggest using one of the several that have already been written and debugged.
I'm just pointing out, that in order to "fix" your issue you need to return the then this way:
function doSomething() {
//xhr here
return {
then: function(callback) {
setTimeout(function() {
callback(42); // callback from within the async action
}, 10);
}
};
}
doSomething().then(function(result) {
log("got a result", result);
});
Please read TJ's answer and consider using a promise library - also consider reading this post that explains how promise resolution looks like.
Namely: Your then needs to in turn return a promise when called (rather than just set a timeout) for chaining, and the callback should be assimilated before waiting for it.
Related
I have been going in circles trying to deeply understand JS promises and I can't seem to find a simple answer on how in the world does it passes the value of the resolved to the handle success variable, I don't understand is that as a build method? But the name is (resolvedValue)
For example, I saw also in another example that they used (rejectionReason) to handle the rejected value, I want to understand how that happens.
here is the code:
const prom = new Promise((resolve, reject) => {
resolve('Yay!');
});
// this resolvedValue how does it happen?
const handleSuccess = (resolvedValue) => {
console.log(resolvedValue);
};
prom.then(handleSuccess); // Prints: 'Yay!'
First lets clarify on two different concepts:
Function Definition:
const exampleFunction=(name)=>{console.log(name)} // just defined, does nothing
Function Invocation:
exampleFunction("Erfan") // logs "Erfan"
In JavaScript, function definitions can be passed to another function ("a higher order function") as a parameter, usually we describe such situation as "the function taking a callback".
The higher order function then calls the callback according to its own implementation, and may or may not pass a value to it.
In your example, method then of the the prom is the higher order function. it takes a callback, passes the resolve value to it, and calls it. it doesn't care about its name, just looks for a function definition and calls it.
Credits to got to #Nirjal Paudel reference link, the answer to my question is :
it happens by paassing it in the value as an anonymous function.
Promises have a method called then that will run after a promise reaches resolve in the code. then will return the promise’s value as a parameter.
This is how you would return and log the value of the example promise:
const handleSuccess = (resolvedValue) => {
console.log(resolvedValue);
};
prom.then(handleSuccess);
The promise you created had a [[PromiseValue]] of 'Yay!'. This value is what will be passed into the anonymous function as a response
so this means that the name won't matter
I have a validation library that is purely synchronous but is often used as part of a chain of async functions. However, I have to maintain an existing synchronous API and would like to make the promise API optional.
Can I somehow detect (at runtime) whether a function is part of a Promise chain?
This is pretty easy with callbacks since you can just check if a callback was passed in. I understand that I could pass in an optional promise boolean, but that seems inelegant.
I've also considered doing a callback interface and using a library to convert the callback interface to a promise based interface on the fly. However, I'm working in Haxe and I would prefer to keep the transforms/abstractions down to a minimum.
I also understand that you can sandwich regular functions in-between promises, but there are some instances where the behavior would differ between the two.
FINAL Edit Lots of confusion as to why I can't just return the same values, first example (below) didn't seem to help. Remember, this is still simplified.
//mix of sync with promise
new Promise(function(resolve, reject){
var safeToAdd = thingTracker.preflight(newThing);
if(safeToAdd){
return client.request.addThing(newThing); //send request to server
} else {
reject(newThing.errorMessages); //requires explicit reject, cannot just pass results along
}
}).then(function(newThing){ //client and server both cool with newThing?
thingTracker.save(newThing);
}).catch(function(errorMessages){ //handles errorMessages from client and server
ui.show(errorMessages);
});
//pure promise
thingTracker.preflight(newThing).then(function(){
return client.request.addThing(newThing); //sends request to server
}).then(function(newThing){ //client and server both cool with newThing?
thingTracker.save(newThing);
}).catch(function(errorMessages){ //handles errorMessages from client and server
ui.show(errorMessages);
});
(Old) Edit to clarify (but didn't really):
function preflight(thing){
var validity = thing === 42;
if(promise){
if(validity){
return Promise.resolve(validity);
} else {
return Promise.reject(validity);
}
} else {
return validity;
}
}
Obviously I can do the same checks in a then's anon function, but that is not much better than using the sync interface directly. Also note that this is a very simple example, in the real function there are side effects on thing and messages are produced.
Edit Just to illustrate my point a bit better, here is a gist of what things look like.
Synchronous functions can be used within a promise chain just fine. If you have a synchronous API call that is called like this:
var info = myApi("foo");
That can be used just fine within a promise chain:
someAsyncCall().then(myApi).then(someOtherFunction)
You don't need to make myApi async or return a promise to be used this way. The only thing you can't do in a promise chain with a synchronous function is that it can't be the first item in the chain, but then it doesn't need to be. Since it's synchronous, it can just be called before you start the chain if anyone wants it to execute first. Or, worst case, you can do:
Promise.resolve().then(myAPI).then(someOtherFunction);
Can I somehow detect (at runtime) whether a function is part of a
Promise chain?
No, you cannot (unless you explicitly pass it some info that tells it that) and you do not need to in order to use it in a promise chain. You do not need to return a promise to be a .then() handler. You can just return a value and that value will become the value of the promise chain at that point in the chain.
I also understand that you can sandwich regular functions in-between
promises, but there are some instances where the behavior would differ
between the two ... such as returning false vs throwing a message.
It is not clear to me why the behavior would have to be different. If returning false is your normal synchronous behavior, you can do that just fine in a promise chain - the next step in the chain just needs to handle the false value that is passed to it, just like the next line of synchronous code would do. If throwing an exception is your normal behavior, you can do that just fine in a promise chain too (the promise chain will turn the exception into a rejection) which the following code can decide how it wants to handle. One can make a good argument that it would be worse if your synchronous function behaved differently based on whether it was part of a promise chain or not. If you see a good reason for two different behaviors, then you should probably have either two different functions (that can be documented differently) or an option passed in that determines the behavior.
Comment based on the code you added in your edit
You seem to think that when called via a promise chain that you need to return a resolved or rejected promise chain. You do not need to do that. You can just return a normal value and the promise chain will inherit the value you return. There is really no reason to return a resolved or rejected promise unless you want it to be the first function in a promise chain, but in that case, it isn't in a promise chain yet anyway so you could never detect that. And, there's no reason for a synchronous operation to be the first one in a promise chain. Just call the synchronous operation first and then initiation your promise chain with the async operations.
Here's a demo of a synchronous function being used in a promise chain:
function log(str) {
var div = document.createElement("div");
div.innerHTML = str;
document.body.appendChild(div);
}
// test async call
function delay(t, val) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(val);
}, t);
});
}
function square(x) {
return x * x;
}
log("Calculating the square of 9");
delay(500, 9).then(square).then(function(result) {
log("Result = " + result);
});
Or, you can even do this to initiate a promise chain with your synchronous operation as the first operation in the chain:
function log(str) {
var div = document.createElement("div");
div.innerHTML = str;
document.body.appendChild(div);
}
function square(x) {
return x * x;
}
log("Calculating the square of 9");
Promise.resolve(9).then(square).then(function(result) {
log("Result = " + result);
});
I believe promises have only one argument so it's not possible to pass an isPromise boolean through the chain, even if you could, you'd have to consciously add the parameter, so why not just call the async function...
function preflight(thing){
return thing === 42;
}
function preflightAsync(thing){
return new Promise(resolver);
function resolver(resolve,reject){
setTimeout(function foo(){
preflight(thing) ? resolve(true,"this won't make it") : reject();
},1000);
}
}
// Promised
preflightAsync(42).then(doSomethingWithIt).catch(doSomethingWithIt);
preflightAsync(89).then(doSomethingWithIt).catch(doSomethingWithIt);
// Call Directly
doSomethingWithIt(preflight(42));
doSomethingWithIt(preflight(89));
function doSomethingWithIt(result,nada){
result = result ? "BAM!!!" : "WAM!!!";
console.log("doSomethingWithIt",result,nada);//doSomethingWithIt BAM!!! undefined
return result;
}
These examples use the new native Promise function, browser support is limited.
As we can see from previous answers you can't use exactly the same function also for Promises. Adding my workaround here which is unfortunately extending the function prototype ...
The problem is that if you want to use the same function synchronously and in a Promise the use of arguments would differ.
As an example - a function truncating a string:
sync:
truncate('A very long string coming from a sync. operation', 10);
async:
Promise.resolve('A very long string coming from a network request')
.then( truncate(10) )
The async version would make the truncate Function think that 10 is the string and you would need to check typeof in every function or do ducktyping.
What would work:
Promise.resolve('A very long string coming from a network request')
.then( truncate.it(10) )
if you extended the function prototype before:
Function.prototype.it = (function() {
return function() {
var args = arguments;
return function(){
Array.prototype.push.apply(arguments, args);
return this.apply(null, arguments);
}.bind(this);
};
}());
function foo(a, b, c) {
console.log('a is ' + a, ' | b is ' + b, ' | c is ' + c);
};
// LOGIC :
foo(1, 2, 3);
// SAME AS
foo.it(2, 3)(1);
// OR
foo.it(3)(1, 2);
// OR pretty useful for .then()
Promise.resolve(1).then(foo.it(2, 3));
fiddle
I have several async functions with varying numbers of parameters, in each the last param is a callback. I wish to call these in order. For instance.
function getData(url, callback){
}
function parseData(data, callback){
}
By using this:
Function.prototype.then = function(f){
var ff = this;
return function(){ ff.apply(null, [].slice.call(arguments).concat(f)) }
}
it is possible to call these functions like this, and have the output print to console.log.
getData.then(parseData.then(console.log.bind(console)))('/mydata.json');
I've been trying to use this syntax instead, and cannot get the Then function correct. Any ideas?
getData.then(parseData).then(console.log.bind(console))('/mydata.json');
Implementing a function or library that allows you to chain methods like above is a non-trivial task and requires substantial effort. The main problem with the example above is the constant context changing - it is very difficult to manage the state of the call chain without memory leaks (i.e. saving a reference to all chained functions into a module-level variable -> GC will never free the functions from memory).
If you are interested in this kind of programming strategy I highly encourage you to use an existing, established and well-tested library, like Promise or q. I personally recommend the former as it attempts to behave as close as possible to ECMAScript 6's Promise specification.
For educational purposes, I recommend you take a look at how the Promise library works internally - I am quite sure you will learn a lot by inspecting its source code and playing around with it.
Robert Rossmann is right. But I'm willing to answer purely for academic purposes.
Let's simplify your code to:
Function.prototype.then = function (callback){
var inner = this;
return function (arg) { return inner(arg, callback); }
}
and:
function getData(url, callback) {
...
}
Let's analyze the types of each function:
getData is (string, function(argument, ...)) → null.
function(argument, function).then is (function(argument, ...)) → function(argument).
That's the core of the problem. When you do:
getData.then(function (argument) {}) it actually returns a function with the type function(argument). That's why .then can't be called onto it, because .then expects to be called onto a function(argument, function) type.
What you want to do, is wrap the callback function. (In the case of getData.then(parseData).then(f), you want to wrap parseData with f, not the result of getData.then(parseData).
Here's my solution:
Function.prototype.setCallback = function (c) { this.callback = c; }
Function.prototype.getCallback = function () { return this.callback; }
Function.prototype.then = function (f) {
var ff = this;
var outer = function () {
var callback = outer.getCallback();
return ff.apply(null, [].slice.call(arguments).concat(callback));
};
if (this.getCallback() === undefined) {
outer.setCallback(f);
} else {
outer.setCallback(ff.getCallback().then(f));
}
return outer;
}
This looks like an excellent use for the Promise object. Promises improve reusability of callback functions by providing a common interface to asynchronous computation. Instead of having each function accept a callback parameter, Promises allow you to encapsulate the asynchronous part of your function in a Promise object. Then you can use the Promise methods (Promise.all, Promise.prototype.then) to chain your asynchronous operations together. Here's how your example translates:
// Instead of accepting both a url and a callback, you accept just a url. Rather than
// thinking about a Promise as a function that returns data, you can think of it as
// data that hasn't loaded or doesn't exist yet (i.e., promised data).
function getData(url) {
return new Promise(function (resolve, reject) {
// Use resolve as the callback parameter.
});
}
function parseData(data) {
// Does parseData really need to be asynchronous? If not leave out the
// Promise and write this function synchronously.
return new Promise(function (resolve, reject) {
});
}
getData("someurl").then(parseData).then(function (data) {
console.log(data);
});
// or with a synchronous parseData
getData("someurl").then(function (data) {
console.log(parseData(data));
});
Also, I should note that Promises currently don't have excellent browser support. Luckily you're covered since there are plenty of polyfills such as this one that provide much of the same functionality as native Promises.
Edit:
Alternatively, instead of changing the Function.prototype, how about implementing a chain method that takes as input a list of asynchronous functions and a seed value and pipes that seed value through each async function:
function chainAsync(seed, functions, callback) {
if (functions.length === 0) callback(seed);
functions[0](seed, function (value) {
chainAsync(value, functions.slice(1), callback);
});
}
chainAsync("someurl", [getData, parseData], function (data) {
console.log(data);
});
Edit Again:
The solutions presented above are far from robust, if you want a more extensive solution check out something like https://github.com/caolan/async.
I had some thoughts about that problem and created the following code which kinda meets your requirements. Still - I know that this concept is far away from perfect. The reasons are commented in the code and below.
Function.prototype._thenify = {
queue:[],
then:function(nextOne){
// Push the item to the queue
this._thenify.queue.push(nextOne);
return this;
},
handOver:function(){
// hand over the data to the next function, calling it in the same context (so we dont loose the queue)
this._thenify.queue.shift().apply(this, arguments);
return this;
}
}
Function.prototype.then = function(){ return this._thenify.then.apply(this, arguments) };
Function.prototype.handOver = function(){ return this._thenify.handOver.apply(this, arguments) };
function getData(json){
// simulate asyncronous call
setTimeout(function(){ getData.handOver(json, 'params from getData'); }, 10);
// we cant call this.handOver() because a new context is created for every function-call
// That means you have to do it like this or bind the context of from getData to the function itself
// which means every time the function is called you have the same context
}
function parseData(){
// simulate asyncronous call
setTimeout(function(){ parseData.handOver('params from parseData'); }, 10);
// Here we can use this.handOver cause parseData is called in the context of getData
// for clarity-reasons I let it like that
}
getData
.then(function(){ console.log(arguments); this.handOver(); }) // see how we can use this here
.then(parseData)
.then(console.log)('/mydata.json'); // Here we actually starting the chain with the call of the function
// To call the chain in the getData-context (so you can always do this.handOver()) do it like that:
// getData
// .then(function(){ console.log(arguments); this.handOver(); })
// .then(parseData)
// .then(console.log).bind(getData)('/mydata.json');
Problems and Facts:
the complete chain is executed in the context of the first function
you have to use the function itself to call handOver at least with the first Element of the chain
if you create a new chain using the function you already used, it will conflict when it runs to the same time
it is possible to use a function twice in the chain (e.g. getData)
because of the shared conext you can set a property in one function and read it in one of the following functions
At least for the first Problem you could solve it with not calling the next function in the chain in the same context and instead give the queue as parameter to the next function. I will try this approach later. This maybe would solve the conflicts mentioned at point 3, too.
For the other problem you could use the sample Code in the comments
PS: When you run the snipped make sure your console is open to see the output
PPS: Every comment on this approach is welcome!
The problem is that then returns a wrapper for the current function and successive chained calls will wrap it again, instead of wrapping the previous callback. One way to achieve that is to use closures and overwrite then on each call:
Function.prototype.then = function(f){
var ff = this;
function wrapCallback(previousCallback, callback) {
var wrapper = function(){
previousCallback.apply(null, [].slice.call(arguments).concat(callback));
};
ff.then = wrapper.then = function(f) {
callback = wrapCallback(callback, f); //a new chained call, so wrap the callback
return ff;
}
return wrapper;
}
return ff = wrapCallback(this, f); //"replace" the original function with the wrapper and return that
}
/*
* Example
*/
function getData(json, callback){
setTimeout( function() { callback(json) }, 100);
}
function parseData(data, callback){
callback(data, 'Hello');
}
function doSomething(data, text, callback) {
callback(text);
}
function printData(data) {
console.log(data); //should print 'Hello'
}
getData
.then(parseData)
.then(doSomething)
.then(printData)('/mydata.json');
I have a situation where my WinJS app wants to call a function which may or may not be async (e.g. in one situation I need to load some data from a file (async) but at other times I can load from a cache syncronously).
Having a look through the docs I though I could wrap the conditional logic in a promise like:
A)
return new WinJS.Promise(function() { // mystuff });
or possibly use 'as' like this:
B)
return WinJS.Promise.as(function() { // mystuff });
The problem is that when I call this function, which I'm doing from the ready() function of my first page like this:
WinJS.UI.Pages.define("/pages/home/home.html", {
ready: function () {
Data.Survey.init().done(function (result) {
// do some stuff with 'result'
});
}
});
When it is written like 'A' it never hits my done() call.
Or if I call it when it's written like 'B', it executes the code inside my done() instantly, before the promise is resolved. It also looks from the value of result, that it has just been set to the content of my init() function, rather than being wrapped up in a promise.
It feels like I'm doing something quite basically wrong here, but I'm unsure where to start looking.
If it's any help, this is a slimmed down version of my init() function:
function init() {
return new WinJS.Promise(function() {
if (app.context.isFirstRun) {
app.surveyController.initialiseSurveysAsync().then(function (result) {
return new WinJS.Binding.List(result.surveys);
});
} else {
var data = app.surveyController.getSurveys();
return new WinJS.Binding.List(data);
}
});
}
Does anyone have any thoughts on this one? I don't believe the 'may or may not be async' is the issue here, I believe the promise setup isn't doing what I'd expect. Can anyone see anything obviously wrong here? Any feedback greatly appreciated.
Generally speaking, if you're doing file I/O in your full init routine, those APIs return promises themselves, in which case you want to return one of those promises or a promise from one of the .then methods.
WinJS.Promise.as, on the other hand, is meant to wrap a value in a promise. But let me explain more fully.
First, read the documentation for the WinJS.Promise constructor carefully. Like many others, you're mistakenly assuming that you just wrap a piece of code in the promise and voila! it is async. This is not the case. The function that you pass to the constructor is an initializer that receives three arguments: a completeDispatcher function, an errorDispatcher function, and a progressDispatcher function, as I like to call them.
For the promise to ever complete with success, complete with an error, or report progress, it is necessary for the rest of the code in the initializer to eventually call one of the dispatchers. These dispatchers, inside the promise, then loop through and call any complete/error/progress methods that have been given to that promise's then or done methods. Therefore, if you don't call a dispatcher at all, there is no completion, and this is exactly the behavior you're seeing.
Using WinJS.Promise.as is similar in that it wraps a value inside a promise. In your case, if you pass a function to WinJS.promise.as, what you'll get is a promise that's fulfilled with that function value as a result. You do not get async execution of the function.
To achieve async behavior you must either use setTimeout/setInterval (or the WinJS scheduler in Windows 8.1) to do async work on the UI thread, or use a web worker for a background thread and tie its completion (via a postMessage) into a promise.
Here's a complete example of creating a promise using the constructor, handling complete, error, and progress cases (as well as cancellation):
function calculateIntegerSum(max, step) {
if (max < 1 || step < 1) {
var err = new WinJS.ErrorFromName("calculateIntegerSum", "max and step must be 1 or greater");
return WinJS.Promise.wrapError(err);
}
var _cancel = false;
//The WinJS.Promise constructor's argument is a function that receives
//dispatchers for completed, error, and progress cases.
return new WinJS.Promise(function (completeDispatch, errorDispatch, progressDispatch) {
var sum = 0;
function iterate(args) {
for (var i = args.start; i < args.end; i++) {
sum += i;
};
//If for some reason there was an error, create the error with WinJS.ErrorFromName
//and pass to errorDispatch
if (false /* replace with any necessary error check -- we don’t have any here */) {
errorDispatch(new WinJS.ErrorFromName("calculateIntegerSum", "error occurred"));
}
if (i >= max) {
//Complete--dispatch results to completed handlers
completeDispatch(sum);
} else {
//Dispatch intermediate results to progress handlers
progressDispatch(sum);
//Interrupt the operation if canceled
if (!_cancel) {
setImmediate(iterate, { start: args.end, end: Math.min(args.end + step, max) });
}
}
}
setImmediate(iterate, { start: 0, end: Math.min(step, max) });
},
//Cancellation function
function () {
_cancel = true;
});
}
This comes from Appendix A ("Demystifying Promises") of my free ebook, Programming Windows Store Apps in HTML, CSS, and JavaScript, Second Edition (in preview), see http://aka.ms/BrockschmidtBook2.
You would, in your case, put your data initialization code in the place of the iterate function, and perhaps call it from within a setImmediate. I encourage you to also look at the WinJS scheduler API that would let you set the priority for the work on the UI thread.
In short, it's essential to understand that new WinJS.Promise and WinJS.Promise.as do not in themselves create async behavior, as promises themselves are just a calling convention around "results to be delivered later" that has nothing inherently to do with async.
I wanted an easy way to add pauses into some stuff that I chain together with promises.
I thought it would be nice to include a "wait" method so I could write stuff like:
var promise = new WinJS.Promise(function(complete){
whatever()
}).wait(1000).then(function(){
whatever2();
}).wait(500).done(function(){
alldone();
}
So to do this I added a wait method to the Promise class like so:
if (WinJS.Promise.prototype.wait == null) {
WinJS.Promise.prototype.wait = function (milliseconds) {
var promise = new WinJS.Promise(function (complete) {
setTimeout(complete, milliseconds);
});
return promise;
}
}
It seemed to be working, but I noticed that if I use a "then", the object I get back from it, while the documentation says is a WinJS.Promise, won't have a wait function. The promises I create all DO have the wait function, but calling .then() on a promise will cause the subsequent .wait to fail, so...
promise.wait(300).then().done();
is no problem but:
promise.then().wait(300).done();
will error out saying that there is no wait method on the Promise returned from then().
Can anyone explain what I'm doing wrong?
There are two reasons why your code doesn't work.
The first is that Microsoft has used their own approach to creating object instances, which they do through the WinJS.Class namespace - this means that the prototype you are altering with the addition of your wait function doesn't ever get applied to the WinJS.Promise objects you are consuming in your code.
The second problem is that you are targeting the wrong object - the WinJS.Promise.then method calls WinJS.Promise.as which returns a CompletePromise object - so even if you could make your wait function stick, it would be in the wrong place. The CompletePromise definition is not in the public scope, so you'd have to do a lot of hacking to be able make the change you want.
There is a solution, but you have to use the WinJS.Promise.timeout method. You can't use this inline, which means that to get the effect you want, you will need some slightly awkward code, as follows;
var promise = new WinJS.Promise(function (complete) {
whatever();
complete();
}).then(function () {
return WinJS.Promise.timeout(1000);
}).then(whatever2).then(function() {
return WinJS.Promise.timeout(500);
}).then(alldone);
This not a direct answer to your question. Adding a wait() method to Promise's prototype should indeed work, unless then() returns an object that looks like a Promise, quacks like a Promise, but is not actually a Promise.
That said, you do not have to implement a wait() method in the first place, because Promise already exposes a timeout() method that does the same thing (and more, actually). You're looking for its single-argument form:
var promise = new WinJS.Promise(function(complete) {
whatever();
}).timeout(1000).then(function() {
whatever2();
}).timeout(500).done(function() {
alldone();
});