Perhaps i'm not googleing correctly. does a then function without a paramater not block? for instance, you have a promise:
someFunc = () => {
return new Promise((res,rej)=>{
somethingAsync(input).then((val) => res(val))
})
}
in the following implements of our function. would both wait for the someFunc return val?
someFunc().then(dosomethingafter())
someFunc().then((val) => dosomethingafter())
In JS expressions are eagerly evaluated. It means every function argument is evaluated before it's passed.
someFunc().then(dosomethingafter())
is effectively identical to
var tmp = dosomethingafter();
someFunc().then(tmp)
so a function someFunc().then(dosomethingafter()) is invoked before then is called, and its returned result is passed as a parameter.
What you probably meant instead is
someFunc().then(dosomethingafter)
note there is no function call - only a reference to a function is passed to then and it would then be called when a promise is resolved.
It is easier to illustrate this via examples. Your first case:
const fn = (text) => {console.log(text)}
const waiter = () => new Promise((resolve, reject) => {
return setTimeout(() => {
fn('resolved')
resolve()
}, 2000)
})
waiter().then(fn('done'))
Notice that fn got executed first, got evaluated and then the waiter got executed.
Lets look at the 2nd case:
const fn = (text) => {console.log(text)}
const waiter = () => new Promise((resolve, reject) => {
return setTimeout(() => {
fn('resolved')
resolve()
}, 2000)
})
waiter().then(() => fn('done'))
Notice now that we got resolved first and then done.
So the answer to your question is yes in both cases we will wait and execute the someFunc or in the above example the waiter.
The main difference really is when does your dosomethingafter get executed.
In the first case it is right away and then it is passed in the waiter.
In the second case you have a valid promise chain which will first be executed and then once done (and since fn acts as the function handler of the then) it will execute dosomethingafter.
pass doSomethingafter first class
const handleAsJson = response => response.json()
fetch(url)
.then(handleAsJson)
.then(console.log)
Related
I'm following the example in this article https://spin.atomicobject.com/2018/09/10/javascript-concurrency/:
What we need here is basically a mutex: a way to say that the critical section of reading the collection, updating it, and writing it back cannot be happening simultaneously. Let’s imagine we had such a thing [...]:
const collectionMutex = new Mutex();
async function set(collection: string, key: string, value: string): {[key: string]: string} {
return await collectionMutex.dispatch(async () => {
const data = await fetchCollection(collection);
data[key] = val;
await sendCollection(collection, data);
return data;
});
}
Implementing this mutex requires a bit of promise-trampoline-ing, but it’s still relatively straightforward:
class Mutex {
private mutex = Promise.resolve();
lock(): PromiseLike<() => void> {
let begin: (unlock: () => void) => void = unlock => {};
this.mutex = this.mutex.then(() => {
return new Promise(begin);
});
return new Promise(res => {
begin = res;
});
}
async dispatch(fn: (() => T) | (() => PromiseLike<T>)): Promise<T> {
const unlock = await this.lock();
try {
return await Promise.resolve(fn());
} finally {
unlock();
}
}
}
In the dispatch function of the Mutex class, unlock is set to await this.lock().
My question is: how and why is unlock a function when lock() returns a Promise that doesn't resolve to anything; the Promise just sets begin = res.
Here is how it works:
There are two promises involved that are created with new Promise. One of them is the promise that lock returns. Let's call that promise P1. The other one is created later, in the callback passed to this.mutex.then. Let's call that promise P2.
res is a function which, when called, resolves P1. But it is not called immediately. Instead, begin is made to reference that same function (begin = res) so we can access it later.
When the callback given to this.mutex.then gets executed (which is when the most recent lock is released), the main magic happens:
new Promise(begin) will execute begin. It looks strange, as normally you would provide an inline callback function where you would perform the logic that has some asynchronous dependency and then have it call resolve -- the argument that this callback function gets. But here begin is that callback function. You could write the creation of the P2 promise more verbose, by providing an inline function wrapper to the constructor, like this:
new Promise(resolve => begin(resolve));
As indicated above, calling begin will resolve P1. The argument passed to begin will be the resolve function that the promise constructor provides to us so we can resolve that new P2 promise. This (function) argument thus becomes the resolution value for P1, and yes, it is a function. This is what the await-ed expression resolves to. unlock is thus a resolve function for resolving P2.
I'm not looking for jQuery solutions please. I tried using the Promise method, and it wouldn't work either I'm trying the callback method but no luck. For right now, as a test I just want it to print something to the console. I'll do the actual code after.
I have a JSFIDDLE HERE for your coding pleasure
This is the callback method I am trying now as I think it's the simplest
const logoAnimation = () => {
this.logo.classList.add('fade-out-zoom-out');
this.blueCnt.classList.add('fade-out');
this.circWrapper.classList.add('dashed-border');
this.clickAbove.classList.add('text-fade-out');
this.welcome.classList.add('text-fade-in');
}
const rotateBorder = () => {
console.log('I run after animation')
}
const render = () => {
console.log(logoAnimation())
logoAnimation(() => {
rotateBorder();
})
}
I did try the Promise method... I'll take either one to be frank (An explanation of it would be ever better please)
function logoAnimation() {
return new Promise(function(resolve, reject) {
this.logo.classList.add('fade-out-zoom-out');
this.blueCnt.classList.add('fade-out');
this.circWrapper.classList.add('dashed-border');
this.clickAbove.classList.add('text-fade-out');
this.welcome.classList.add('text-fade-in');
})
}
const rotateBorder = () => {
console.log('I run after animation')
}
function render() {
logoAnimation().then(function () {
rotateBorder()
})
}
Then just an onclick call from somewhere
<img class="logo" id="logo" onclick="render()" src="/path/img"/>
Your problem is essentially the same in both versions.
In the first one the implementation of logoAnimation doesn't care about the callback you pass it - in fact it doesn't accept any arguments at all, so it will happily ignore anything passed to it. If you intend to pass it a callback, then you need to ensure that you call it ("call it back" - this is where the term "callback" comes from!) inside the function:
const logoAnimation = (callback) => {
this.logo.classList.add('fade-out-zoom-out');
this.blueCnt.classList.add('fade-out');
this.circWrapper.classList.add('dashed-border');
this.clickAbove.classList.add('text-fade-out');
this.welcome.classList.add('text-fade-in');
callback();
}
In the Promise version, you likewise never call resolve, which is the function passed in to the .then handler. So to get that version to work, you would need to change the implementation like this:
function logoAnimation() {
return new Promise(function(resolve, reject) {
this.logo.classList.add('fade-out-zoom-out');
this.blueCnt.classList.add('fade-out');
this.circWrapper.classList.add('dashed-border');
this.clickAbove.classList.add('text-fade-out');
this.welcome.classList.add('text-fade-in');
resolve();
})
}
I will note that, although in general I'm a big fan of using Promises for asynchronous code rather than old-style spaghetti callbacks, there's nothing asynchronous going on in your examples so I see no need for Promises - I'd just stick with the callback-based version, assuming you need callers of logoAnimation to specify what should happen next.
Unfortunately, css transitions are out of control of JavaScript. Your problem is not with the order of functions.
In order to achieve what you want, you should use setTimeout function and calculate those durations yourself.
You can use a "wait" function in order to mimic waiting:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
let logo = document.getElementById('logo');
let blueCnt = document.getElementById('blueCnt');
let circWrapper = document.getElementById('circWrapper');
let clickAbove = document.getElementById('clickAbove');
let welcome = document.getElementById('welcome');
function logoAnimation() {
this.logo.classList.add('fade-out-zoom-out');
this.blueCnt.classList.add('fade-out');
this.circWrapper.classList.add('dashed-border');
this.clickAbove.classList.add('text-fade-out');
this.welcome.classList.add('text-fade-in');
}
const rotateBorder = () => {
console.log('I run after animation')
}
function render() {
logoAnimation()
wait(1000).then(rotateBorder)
}
Also pay attention that if you do not call the resolve function inside a Promise callback, it won't fulfill and your function call won't reach the chained .then
If you want to run 2 synchronous functions in sequence, you can just run them one at a time:
func1();
func2(); // Only runs after func1 has completed.
A quick and dirty solution would be to use a timeout:
const logoAnimation = () => {
this.logo.classList.add('fade-out-zoom-out');
this.blueCnt.classList.add('fade-out');
this.circWrapper.classList.add('dashed-border');
this.clickAbove.classList.add('text-fade-out');
this.welcome.classList.add('text-fade-in');
setTimeout(()=>{
rotateBorder();
}, 3000);
}
I have an async function f that calls another async function g. To test if f calls g, I'm stubbing g using sinon and assert it's been called using should.js.
'use strict';
require('should-sinon');
const sinon = require('sinon');
class X {
async f(n) {
await this.g(n);
// this.g(n); // I forget to insert `await`!
}
async g(n) {
// Do something asynchronously
}
}
describe('f', () => {
it('should call g', async () => {
const x = new X();
sinon.stub(x, 'g').resolves();
await x.f(10);
x.g.should.be.calledWith(10);
});
});
But this test passes even when I forget to use await when calling g in f.
One of the ways to catch this error is to make the stub return a dummy promise and check if its then is called.
it('should call g', async () => {
const x = new X();
const dummyPromise = {
then: sinon.stub().yields()
};
sinon.stub(x, 'g').returns(dummyPromise);
await x.f(10);
x.g.should.be.calledWith(10);
dummyPromise.then.should.be.called();
});
But this is a bit bothersome. Are there any convenient ways to do this?
Your example for f shows flawed code design which becomes more obvious if you write the same function without async/await syntax:
f(n) {
return g(n).then(()=>{});
}
This achieves the same behavior - whether g resolved becomes hard to tell (assuming you don't know if f returned g's promise, which is the same as not knowing whether f awaited g). If f is not interested in the result of g it should just simply return it, not hide it. Then you can simply test for the result.
If your point is that f might have to trigger several async calls sequentially awaiting several g_1, g_2,... to resolve, then you can build a test chain by asserting in the stub of g_n+1 that the dummy-promise of g_n has been resolved. In general your approach to test a dummy-promise for its status is fine.
Instead of stubbing then, you're best off stubbing g in such a way that it sets some boolean on the next event loop iteration. Then, you can check this boolean after calling f to make sure f waited for it:
it('should call g', async () => {
const x = new X();
let gFinished = false;
sinon.stub(x, 'g').callsFake(() => {
return new Promise((resolve) => {
setImmediate(() => {
gFinished = true;
resolve();
});
});
});
await x.f(10);
x.g.should.be.calledWith(10);
gFinished.should.be.true();
});
Edit: Of course, this isn't a perfect guarantee because you could have f wait on any promise that waits at least as long as it takes for g to resolve. Like so:
async f(n) {
this.g(n);
await new Promise((resolve) => {
setImmediate(() => {
resolve();
});
});
}
This would cause the test I wrote to pass, even though it's still incorrect. So really it comes down to how strict you're trying to be with your tests. Do you want it to be literally impossible to have a false positive? Or is it ok if some obvious trickery can potentially throw it off?
In most cases I find that the latter is ok, but really that's up to you and/or your team.
I have a series of promise functions that I'm chaining together. I don't need the specific result from the previous function so I'm not adding anything into resolve(); I just need them to run sequentially.
However, there is a variable in the containing scope that I want to pass into the first and fourth(last) promise. When I add it as a parameter to the third promise, the function runs immediately.
How can I pass a parameter into a chained promise and not call the function/promise at that time?
Here is the basics of what I'm trying to do:
const containingFunction = function() {
const varToPass = document.querySelector("#ID").value.trim();
firstPromise(varToPass)
.then(secondPromise)
.then(thirdPromise)
.then(fourthPromise(varToPass))
.catch(e =>{
console.error(e));
} );
};
FourthPromise:
const fourthPromise = function(varPassed) {
return new Promise(function(resolve, reject) {
do some stuff but only after the thirdPromise has been resolved
console.log(varPassed)
resolve();
});
};
You have two possibilities depending on how you want to resolve varToPass.
Lambda
Using a lambda function (anonymous function without own scope) as described by #Jaromanda X:
() => return fourthPromise(varToPass)
which will cause the function to keep a reference to the variable and not its value. The value of varToPass will be evaluated as soon as the fourthPromise is fired, not when this code is run.
Wrapper
The second option is using a wrapper, i.e. a function that returns a function:
function fourthPromise(varToPass) {
return function() {
return new Promise(function(resolve, reject) {
do some stuff but only after the thirdPromise has been resolved
console.log(varToPass)
resolve();
});
};
}
In this case the value of the passed variable is evaluated at the time this code runs and not when the callback is called.
Which option is better applicable to your case we can't tell without more context though.
Very simple change
const containingFunction = function() {
const varToPass = document.querySelector("#ID").value.trim();
firstPromise(varToPass)
.then(secondPromise)
.then(thirdPromise)
.then(() => fourthPromise(varToPass))
.catch(e =>{
console.error(e));
} );
};
You have a prototype object Foo with two async method calls, bar and baz.
var bob = new Foo()
Foo.prototype.bar = function land(callback) {
setTimeout(function() {
callback()
console.log('bar');
}, 3000);
};
Foo.prototype.baz = function land(callback) {
setTimeout(function() {
callback()
console.log('baz');
}, 3000);
};
We want to do bob.bar().baz() and have it log "bar" and "baz" sequentially.
If you cannot modify the method calls (including passing in your callback function), how can you pass a default callback into these method calls?
Some ideas:
Wrap "bob" with decorator (still fuzzy on how to implement, could use a small example)
Modify constructor to assign default callback if none assigned (have not considered if this is possible or not)
Use a generator wrapper that will continue to call next method until none are left?
The more recommended way instead is to use promises as this is the community-wide practice to do async work.
We want to do bob.bar().baz() and have it log "bar" and "baz"
sequentially.
Why would you want to do that just to achieve this bob.bar().baz() "syntax"? You could do it pretty neatly using the Promise API w/o additional efforts to make that syntax work that indeed increases code complexity reducing the actual readability.
So, you might want to consider using the promise-based approach like this. It offers much flexibility than what you would have achieved with your approach:
Foo.prototype.bar = function () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve()
console.log('bar');
}, 3000);
};
};
Foo.prototype.baz = function () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve()
console.log('baz');
}, 3000);
};
};
Now you'd do this to run them sequentially one after another:
var bob = new Foo();
bob.bar().then(function() {
return bob.baz();
});
// If you're using ES2015+ you could even do:
bob.bar().then(() => bob.baz());
If you need to chain more functions you could simply do it:
bob.bar()
.then(() => bob.baz())
.then(() => bob.anotherBaz())
.then(() => bob.somethingElse());
Anyway, if you're not used to using promises you might want to read this
Warning this isn't quite right yet. Ideally we'd subclass Promise and have proper then/catch functionality but there are some caveats with subclassing bluebird Promise. The idea is to store an internal array of promise generating functions, then when a Promise is waited on (then/await) serially wait on those promises.
const Promise = require('bluebird');
class Foo {
constructor() {
this.queue = [];
}
// promise generating function simply returns called pGen
pFunc(i,pGen) {
return pGen();
}
bar() {
const _bar = () => {
return new Promise( (resolve,reject) => {
setTimeout( () => {
console.log('bar',Date.now());
resolve();
},Math.random()*1000);
})
}
this.queue.push(_bar);
return this;
}
baz() {
const _baz = () => {
return new Promise( (resolve,reject) => {
setTimeout( () => {
console.log('baz',Date.now());
resolve();
},Math.random()*1000);
})
}
this.queue.push(_baz);
return this;
}
then(func) {
return Promise.reduce(this.queue, this.pFunc, 0).then(func);
}
}
const foo = new Foo();
foo.bar().baz().then( () => {
console.log('done')
})
result:
messel#messels-MBP:~/Desktop/Dropbox/code/js/async-chain$ node index.js
bar 1492082650917
baz 1492082651511
done
If you want to avoid callback hell and keep your sanity ES6 promises are the most appropriate approach for the sake of functional programming. You just chain up your sequential asynchronous tasks in the asynchronous timeline just like working in a synchronous timeline.
In this particular case you just need to promisify your asynchronous functions. Assume that your asynch functions takes a data and a callback like asynch(data,myCallback). Let us assume that the callback is error first type.
Such as;
var myCallback = (error,result) => error ? doErrorAction(error)
: doNormalAction(result)
When your asynch function is promisified, you will actually be returned a function which takes your data and returns a promise. You are expected to apply myCallback at the then stage. The return value of myCallback will then be passed to the next stage at where you can invoke another asynch function supplied with the return value of myCallback and this goes on and on as long as you need. So let's see how we shall implement this abstract to your workflow.
function Foo(){}
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
function myCallback(val) {
console.log("hey..! i've got this:",val);
return val;
}
var bob = new Foo();
Foo.prototype.bar = function land(value, callback) {
setTimeout(function() {
callback(false,value*2); // no error returned but value doubled and supplied to callback
console.log('bar');
}, 1000);
};
Foo.prototype.baz = function land(value, callback) {
setTimeout(function() {
callback(false,value*2); // no error returned but value doubled and supplied to callback
console.log('baz');
}, 1000);
};
Foo.prototype.bar = promisify(Foo.prototype.bar);
Foo.prototype.baz = promisify(Foo.prototype.baz);
bob.bar(1)
.then(myCallback)
.then(bob.baz)
.then(myCallback)