Why does this promise resolve to a function (unlock)? - javascript

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.

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).

How can I assert a function has `await`ed an async function using should.js

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.

Javascript then without promise return val

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)

Why does Typescript think async/await returns a value wrapped in a promise?

I want to refactor a promise chain into async/await, but Typescript is complaining about the typing.
TS2322:Type 'IHttpPromiseCallbackArg< IResp >' is not assignable to type 'IResp'...
I thought await would return a regular value, not a promise. Am I wrong? If so, how can I assign a typing so that the desired code will compile?
I thought await would return the same value as the first argument in the .then callback. Am I wrong?
Old code:
handleSubmit(form) {
const params = this.getParams(form);
this.myAsyncRequest(params)
.then((resp:IResp) => this.processResp(resp))
.catch((e:IServerError) => this.handleError(e));
}
Desired new code:
async handleSubmit(form) {
const params = this.getParams(form);
try {
const resp:IResp = await this.myAsyncRequest(params); //typing error with "const resp:IResp"
this.processResp(resp);
} catch (e:IServerError) {
this.handleError(e);
}
}
The desired code still breaks if I remove the return type in myAsyncRequest ; I guess Typescript infers directly from the AngularJS library.
myAsyncRequest(params:IParams):IHttpPromise<IResp> {
return $http.post('blah', data);
}
If I remove "IResp" from the const resp declaration, processResponse complains that IHttp< IResp> does not equal IResp...
processResp(resp:IResp) {
//do stuff
}
Your question "I thought await would return the same value as the first argument in the .then callback. Am I wrong?".
No, you are absolutely right. But you are wrong about what the first argument in the .then callback is.
You define myAsyncRequest to return IHttpPromise<IResp>. But IHttpPromise<T> is defined as inheriting IPromise the following way:
type IHttpPromise<T> = IPromise<IHttpPromiseCallbackArg<T>>;
So, an IHttpPromise<T> is a promise that returns an IHttpPromiseCallbackArg<T> back where the actual data of type T is in the data property of the IHttpPromiseCallbackArg<T>.
So, the old code variant we see in the question:
handleSubmit(form) {
const params = this.getParams(form);
this.myAsyncRequest(params)
.then((resp:IResp) => this.processResp(resp))
.catch((e:IServerError) => this.handleError(e));
}
should actually not compile without errors in TypeScript when myAsyncRequest is defined to return IHttpPromise.
Howto fix this:
async handleSubmit(form) {
const params = this.getParams(form);
try {
const httpResp:IHttpPromiseCallbackArg<IResp> = await this.myAsyncRequest(params);
const resp: IResp = httpResp.data;
this.processResp(resp);
} catch (e:IServerError) {
this.handleError(e);
}
}
Note: In the latest type definitions for angular, the type IHttpPromiseCallbackArg<T> is actually called IHttpResponse<T>.
Maybe in your code, you have defined IResp as IHttpResponse<Something>? Then you just have a conflict with the old name IHttpPromiseCallbackArg. Then get the newest type definitions from DefinitelyTyped that uses the new name. And you would also have to change the definition of myAsyncRequest to:
myAsyncRequest(params:IParams):IHttpPromise<Something> {
The line containing the await does indeed await the resolved value - but because the function itself is async (to allow all that awaiting), you get back a promise.
Example... in the below code you can use x as a plain number (even though delay returns a promise) and again for y - so all the stuff you await is resolved so you can use it more like you would if it were synchronous.
The async function that doesn't look like it returns a promise, now does.
This can seem confusing, because it seems to "invert the promises", but what it does is shift the then to the top level (you could have async functions calling down to other async functions and so on).
function delay(ms: number) {
return new Promise<number>(function(resolve) {
setTimeout(() => {
resolve(5);
}, ms);
});
}
async function asyncAwait() {
let x = await delay(1000);
console.log(x);
let y = await delay(1000);
console.log(y);
return 'Done';
}
asyncAwait().then((result) => console.log(result));

Difference between async/await and ES6 yield with generators

I was just reading this fantastic article «Generators» and it clearly highlights this function, which is a helper function for handling generator functions:
function async(makeGenerator){
return function () {
var generator = makeGenerator.apply(this, arguments);
function handle(result){
// result => { done: [Boolean], value: [Object] }
if (result.done) return Promise.resolve(result.value);
return Promise.resolve(result.value).then(function (res){
return handle(generator.next(res));
}, function (err){
return handle(generator.throw(err));
});
}
try {
return handle(generator.next());
} catch (ex) {
return Promise.reject(ex);
}
}
}
which I hypothesize is more or less the way the async keyword is implemented with async/await. So the question is, if that is the case, then what the heck is the difference between the await keyword and the yield keyword? Does await always turn something into a promise, whereas yield makes no such guarantee? That is my best guess!
You can also see how async/await is similar to yield with generators in this article where he describes the 'spawn' function ES7 async functions.
Well, it turns out that there is a very close relationship between async/await and generators. And I believe async/await will always be built on generators. If you look at the way Babel transpiles async/await:
Babel takes this:
this.it('is a test', async function () {
const foo = await 3;
const bar = await new Promise(resolve => resolve('7'));
const baz = bar * foo;
console.log(baz);
});
and turns it into this
function _asyncToGenerator(fn) {
return function () {
var gen = fn.apply(this, arguments);
return new Promise(function (resolve, reject) {
function step(key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
return Promise.resolve(value).then(function (value) {
return step("next", value);
}, function (err) {
return step("throw", err);
});
}
}
return step("next");
});
};
}
this.it('is a test', _asyncToGenerator(function* () { // << now it's a generator
const foo = yield 3; // <<< now it's yield, not await
const bar = yield new Promise(resolve => resolve(7));
const baz = bar * foo;
console.log(baz);
}));
you do the math.
This makes it look like the async keyword is just that wrapper function, but if that's the case then await just gets turned into yield, there will probably be a bit more to the picture later on when they become native.
You can see more of an explanation for this here:
https://www.promisejs.org/generators/
yield can be considered to be the building block of await. yield takes the value it's given and passes it to the caller. The caller can then do whatever it wishes with that value (1). Later the caller may give a value back to the generator (via generator.next()) which becomes the result of the yield expression (2), or an error that will appear to be thrown by the yield expression (3).
async-await can be considered to use yield. At (1) the caller (i.e. the async-await driver - similar to the function you posted) will wrap the value in a promise using a similar algorithm to new Promise(r => r(value) (note, not Promise.resolve, but that's not a big deal). It then waits for the promise to resolve. If it fulfills, it passes the fulfilled value back at (2). If it rejects, it throws the rejection reason as an error at (3).
So the utility of async-await is this machinery that uses yield to unwrap the yielded value as a promise and pass its resolved value back, repeating until the function returns its final value.
what the heck is the difference between the await keyword and the yield keyword?
The await keyword is only to be used in async functions, while the yield keyword is only to be used in generator function*s. And those are obviously different as well - the one returns promises, the other returns generators.
Does await always turn something into a promise, whereas yield makes no such guarantee?
Yes, await will call Promise.resolve on the awaited value.
yield just yields the value outside of the generator.
tl;dr
Use async/await 99% of the time over generators. Why?
async/await directly replaces the most common workflow of promise chains allowing code to be declared as if it was synchronous, dramatically simplifying it.
Generators abstract the use case where you would call a series of async-operations that depend on each other and eventually will be in a "done" state. The most simple example would be paging through results that eventually return the last set but you would only call a page as needed, not immediately in succession.
async/await is actually an abstraction built on top of generators to make working with promises easier.
See very in-depth Explanation of Async/Await vs. Generators
Try this test programs which I used to understand await/async with promises.
Program #1: without promises it doesn't run in sequence
function functionA() {
console.log('functionA called');
setTimeout(function() {
console.log('functionA timeout called');
return 10;
}, 15000);
}
function functionB(valueA) {
console.log('functionB called');
setTimeout(function() {
console.log('functionB timeout called = ' + valueA);
return 20 + valueA;
}, 10000);
}
function functionC(valueA, valueB) {
console.log('functionC called');
setTimeout(function() {
console.log('functionC timeout called = ' + valueA);
return valueA + valueB;
}, 10000);
}
async function executeAsyncTask() {
const valueA = await functionA();
const valueB = await functionB(valueA);
return functionC(valueA, valueB);
}
console.log('program started');
executeAsyncTask().then(function(response) {
console.log('response called = ' + response);
});
console.log('program ended');
Program #2: with promises
function functionA() {
return new Promise((resolve, reject) => {
console.log('functionA called');
setTimeout(function() {
console.log('functionA timeout called');
// return 10;
return resolve(10);
}, 15000);
});
}
function functionB(valueA) {
return new Promise((resolve, reject) => {
console.log('functionB called');
setTimeout(function() {
console.log('functionB timeout called = ' + valueA);
return resolve(20 + valueA);
}, 10000);
});
}
function functionC(valueA, valueB) {
return new Promise((resolve, reject) => {
console.log('functionC called');
setTimeout(function() {
console.log('functionC timeout called = ' + valueA);
return resolve(valueA + valueB);
}, 10000);
});
}
async function executeAsyncTask() {
const valueA = await functionA();
const valueB = await functionB(valueA);
return functionC(valueA, valueB);
}
console.log('program started');
executeAsyncTask().then(function(response) {
console.log('response called = ' + response);
});
console.log('program ended');
In many ways, generators are a superset of async/await. Right now async/await has cleaner stack traces than co, the most popular async/await-like generator based lib. You can implement your own flavor of async/await using generators and add new features, like built-in support for yield on non-promises or building it on RxJS observables.
So, in short, generators give you more flexibility and generator-based libs generally have more features. But async/await is a core part of the language, it's standardized and won't change under you, and you don't need a library to use it. I have a blog post with more details on the difference between async/await and generators.
The yield+gen.next()-as-a-language-feature can be used to describe (or implement) the underlying control-flow that await-async has abstracted away.
As other answers suggest, await-as-a-language-feature is (or can be thought of) an implementation on top of yield.
Here is a more intutive understanding for that:
Say we have 42 awaits in an async function, await A -> await B -> ...
Deep down it is equivalent to having yield A -> tries resolve this as a Promise [1]
-> if resolvable, we yield B, and repeat [1] for B
-> if not resolveable, we throw
And so we end up with 42 yields in a generator. And in our controller we simply keep doing gen.next() until it is completed or gets rejected. (ie this is the same as using await on an async function that contains 42 await.)
This is why lib like redux-saga utilizes generator to then pipe the promises to the saga middleware to be resolved all at one place; thus decoupling the Promises constructions from their evaluations, thus sharing close resemblance to the Free Monad.
The idea is to recursively chain then() calls to replicate the behavior of await which allows one to invoke async routines in a synchronous fashion. A generator function is used to yield back control (and each value) from the callee to the caller, which happens to be the _asyncToGenerator() wrapper function.
As mentioned above, this is the trick that Babel uses to create polyfills. I slightly edited the code to make it more readable and added comments.
(async function () {
const foo = await 3;
const bar = await new Promise((resolve) => resolve(7));
const baz = bar * foo;
console.log(baz);
})();
function _asyncToGenerator(fn) {
return function () {
let gen = fn(); // Start the execution of the generator function and store the generator object.
return new Promise(function (resolve, reject) {
function step(func, arg) {
try {
let item = gen[func](arg); // Retrieve the function object from the property name and invoke it. Similar to eval(`gen.${func}(arg)`) but safer. If the next() method is called on the generator object, the item value by the generator function is saved and the generator resumes execution. The value passed as an argument is assigned as a result of a yield expression.
if (item.done) {
resolve(item.value);
return; // The executor return value is ignored, but we need to stop the recursion here.
}
// The trick is that Promise.resolve() returns a promise object that is resolved with the value given as an argument. If that value is a promise object itself, then it's simply returned as is.
return Promise.resolve(item.value).then(
(v) => step("next", v),
(e) => step("throw", e)
);
} catch (e) {
reject(e);
return;
}
}
return step("next");
});
};
}
_asyncToGenerator(function* () { // <<< Now it's a generator function.
const foo = yield 3; // <<< Now it's yield, not await.
const bar = yield new Promise((resolve, reject) => resolve(7)); // <<< Each item is converted to a thenable object and recursively enclosed into chained then() calls.
const baz = bar * foo;
console.log(baz);
})();

Categories