I found an interest thing about Promise. When I run the following codes, it gave me the output of 'aa' as oppose to 'bb', which confused me a lot. Does anyone understand why and give a explanation, please? Thanks!
Promise.resolve('aa')
.then(Promise.resolve('bb'))
.then(console.log);
Well, you're misusing a .then() handler so it is no surprise that you don't get the desired answer.
A .then() handler should be passed a function reference. You are passing it a promise which it dutifully ignores because it's not a callable function.
When you do this:
.then(Promise.resolve('bb'))
That executes Promise.resolve('bb') immediately and passes the return result (which is a promise) to .then(). So, you're passing a promise to .then() when you should be passing a function reference. If you change your code to this, then you will get what you expect:
Promise.resolve('aa')
.then(function() {return Promise.resolve('bb');})
.then(console.log);
Remember, the point of passing something to .then() is that it can be executed LATER when the parent promise resolves/rejects. So, for that to be possible, you have to pass a function reference that can be called by the promise infrastructure at some later time.
Using ES6 syntax, you could shorten to this:
Promise.resolve('aa')
.then(_ => Promise.resolve('bb))
.then(console.log);
Related
const tasks = [f1, f2, f3];
tasks.reduce(async (promise, task) => {
await promise;
await task();
}, Promise.resolve)
1、The role of Promise.resolve
2、The role of await promise;
Thanks~~
This design pattern using .reduce() is to serialize a number of promise-returning operations. As such, the logic is that you wait on the previous promise, then when it's done you execute your next task and return a promise as value for the next cycle through the loop where the process can be repeated.
To make that first iteration work without special code for the first iteration, you need a promise that you can initially wait for. So, you pass an already resolved promise created with Promise.resolve() to "prime the pump" and give it an initial promise to use.
If you unwrap the .reduce() loop, in your example, you essentially end up with this:
Promise.resolve().then(f1).then(f2).then(f3)
Starting the chain with Promise.resolve() avoids special casing the first iteration of the loop.
That could be written as:
f1().then(f2).then(f3)
But, that special cases the first task which really complicates using something like .reduce() which is simplest when you do the same thing with every iteration of the loop. So, starting things off with Promise.resolve() allows the first iteration to do exactly the same thing as all the other iterations.
As for your two bullet points:
The role of Promise.resolve()
To give .reduce() an initial promise for the first iteration to wait for.
The role of await promise
That waits until the task from the previous iteration of the loop is done before calling the next task.
Note: To fully understand this code, you have to fully understand how .reduce() works. You pass it two arguments, your callback function and an initial value. That initial value is passed to the first iteration of the callback as the first argument to the callback (what you named promise).
Then, whatever value you return from that callback will be passed as the value to the next iteration of the callback. Since you're using an async callback which ALWAYS
returns a promise, your callback will always return a promise which is what will get passed to the next iteration of the callback. And, because the first thing your callback does is await promise, you end up "chaining" promises which serializes the execution of your tasks.
I'm quite new to Kotlin. I hit this part while I was going over the docs:
"a lambda cannot return from the enclosing function" (unless it's inlined).
So, this doesn't work;
fun foo() {
ordinaryFunction {
return // ERROR: cannot make `foo` return here
}
}
I wonder why it works that way?
The only thing I can think of it's dangerous since there might be some extra stuff the enclosing function might be doing after the lambda execution. But I'm not sure that's the reason because you can overcome this by using qualified returns or using inline keyword. So, that kind of implies there's a technical reason behind it (apart from any usability/safety reasons) like the compiler cannot figure out where to return unless it's labeled or inlined.
Any help would be great!
The problem is here that non-local returns can't be done on the JVM.
If you want to return from lambda (local return) you can add label #ordinaryFunction:
fun foo() {
ordinaryFunction {
return#ordinaryFunction
}
}
Docs say:
If we need to return from a lambda expression, we have to label it and qualify the return. Oftentimes it is more convenient to use implicit labels: such a label has the same name as the function to which the lambda is passed. In our case it is #ordinaryFunction.
Someone else can probably explain this better but in pretty much any programming language, when you call a function, a new entry is created on top of the stack. The stack keeps information about the arguments that the function was called with and the place you should return to when the function completes.
Kotlin doesn't have a feature that lets you return from multiple function calls in one return, so you have to return from each function call manually.
When you inline a function the machine code that would normally execute in a separate subroutine is now copy pasted to the function call site instead. That's why return from an inline function actually returns from the function that called the inlined lambda.
there might be some extra stuff the enclosing function might be doing after the lambda execution.
The problem is the other way around: the lambda can "escape" from the enclosing function's scope and end up executing after the function returns. E.g. consider
fun foo() {
Thread(Runnable {
Thread.sleep(1000)
return
})
}
Or just
fun foo() = // lambda
In either case it makes no sense for the lambda to return from foo, does it? And the compiler doesn't know if your ordinaryFunction lets the lambda escape foo's scope unless it's inline.
you can overcome this by using qualified returns
That's not really overcoming, that's just not returning from the enclosing function.
My question is: Is it okay to pass a promise to the first argument of .then? (Assuming that I'm not interested in the returned value of the previous promise and I just want to chain promises together).
Someone told me that if I do this, a new promise will be created
implicitly (unnecessarily), and I might face issues handling errors
bellow in the promise chain.
I know that if you don't explicitly return a promise in an async method the result will be implicitly wrapped. In this case, .then should not wrap the argument in a promise since the argument is already a promise :/
Example:
async function asyncFunc() {
//async function (return Promise)
}
// I know this is okay
somePromise.then(() => asyncFunc());
// BUT... is this okay?
somePromise.then(asyncFunc());
Is it okay to pass a promise to the first argument of .then?
Yes.
Someone told me that if I do this, a new promise will be created implicitly (unnecessarily)
Promise.prototype.then() returns a new promise either way.
// BUT... is this okay?
somePromise.then(asyncFunc());
No, it is more or less the same as:
const p = asyncFunc()
somePromise.then(p);
You execute the function before somePromise actually resolves. What you probably want instead is somePromise.then(asyncFunction). This will properly chain the promises after each other.
Someone told me that [...] I might face issues handling errors bellow in the promise chain.
No. This does not change the behaviour of the promise chain as long as long as there is a catch at the end of the chain.
I created the code below which generates the error: Uncaught TypeError: Cannot read property 'done' of undefined. I think I understand what's happening here, def() isn't a deferred until the timeout expires and the .done tries to run straight away, but what I don't understand is if this doesn't work, why do deferreds work at all? How does .done ever know that the code in front is a promise/deferred given that it may not have resolved yet? Please explain syntax that will make it work, because this is doing my head in.
To put it bluntly I wish that .done would just shut up and wait like it's meant to (I understand that it would have to be a method of all objects and wait until they've resolved even if it's not to a deferred, but if you've typed .done why wouldn't you want that?). Or does it work that way, but I just don't know how to use it properly?
Please don't just refer me to an existing guide/post on jquery deferred unless it explicitly resolves my confusion.
Thanks.
jsfiddle here
function def() {
d = new $.Deferred;
setTimeout(function(){
d.resolve();
return d;
},1000)
}
def().done(function(){
console.log('test');
});
You have to return the promise value from def() itself, not from the setTimeout().
function def() {
var d = $.Deferred();
setTimeout(function(){
d.resolve();
},1000)
return d;
}
def().done(function(){
console.log('test');
});
Returning a value from setTimeout() doesn't do anything. def() has long since finished executing (and returning nothing) and the return value from setTimeout() just goes back into part of the system runtime that calls timer callbacks and is ignored.
I also made a couple other corrections:
Declared d as a local variable (not an implicit global) by putting var in front of its declaration.
Changed the creation of the deferred to $.Deferred(). While, I think the new construction might work, that isn't how jQuery has documented its usage.
I'm writing a simple Twitter client to play with coffeescript. I have an object literal with some functions that call each other via callbacks.
somebject =
foo: 'bar'
authenticateAndGetTweets: ->
console.log "Authorizing using oauth"
oauth = ChromeExOAuth.initBackgroundPage(this.oauthdetails)
oauth.authorize( this.afterLogin.call this )
afterLogin: ->
this.getTweets(this.pollinterval)
This code works perfectly. Edit: actually this.afterlogin should be sent as a callback above, not ran immediately, as Trevor noted below.
If, within authenticateAndGetTweets, I removed the 'call' and just ran:
oauth.authorize( this.afterLogin )
and don't use 'call', I get the following error:
Uncaught TypeError: Object [object DOMWindow] has no method 'getTweets
Which makes sense, since 'this' in afterLogin is bound to the thing that initiated the callback rather than 'someobject' my object literal.
I was wondering if there's some magic in Coffeescript I could be doing instead of 'call'. Initially I thought using the '=>' but the code will give the same error as above if '=>' is used.
So is there a way I can avoid using call? Or does coffeescript not obviate the need for it? What made '=>' not work how I expected it to?
Thanks. I'm really enjoying coffeescript so far and want to make sure I'm doing things 'the right way'.
As matyr points out in his comments, the line
oauth.authorize( this.afterLogin.call this )
doesn't cause this.afterLogin to be called as a callback by oauth.authorize; instead, it's equivalent to
oauth.authorize this.afterLogin()
Assuming that you want this.afterLogin to used as a callback by oauth.authorize, megakorre's answer gives a correct CoffeeScript idiom. An alternative approach supported by many modern JS environments, as matyr points out, would be to write
oauth.authorize( this.afterLogin.bind this )
There's no CoffeeScript shorthand for this, partly because Function::bind isn't supported by all major browsers. You could also use the bind function from a library like Underscore.js:
oauth.authorize( _.bind this.afterLogin, this )
Finally, if you were to define someobject as a class instead, you could use => to define afterLogin such that it's always bound to the instance, e.g.
class SomeClass
foo: 'bar'
authenticateAndGetTweets: ->
console.log "Authorizing using oauth"
oauth = ChromeExOAuth.initBackgroundPage(this.oauthdetails)
oauth.authorize(this.afterLogin)
afterLogin: =>
this.getTweets(this.pollinterval)
someobject = new SomeClass
you can put a lambda in the function call like so
auth.authorize(=> #afterLogin())
You have to use either the call or apply methods because they set the scope of the function (the value of this). The error results because the default scope is the window object.