I would like to implement chained method calls like
observable
.pipe(
filter('foo'),
add(3)
)
.subscribe(subscriber);
In order for this to work, the result of .pipe(...) must provide the method subscribe.
I would like to allow some of the chained method calls (e.g. pipe) to by async. However, this would breaks my chain because the promise returned by pipe does not have a subscribe method:
await observable
.pipe(
filter('foo'),
add(3)
)
.subscribe(subscriber);
async pipe(...operators){
...
}
=> Uncaught (in promise) TypeError: observable.pipe(...).subscribe is not a function
I could rewrite my main code to
observable
.pipe(
filter('foo'),
add(3)
).then(pipeResult=>
pipeResult.subscribe(subscriber);
);
However, I find that very ugly to read.
=> Is there a way to apply await for each call in the chain of method calls and not only for the last one?
I would expect something like
awaitEach observable
.pipe(
filter('foo'),
add(3)
)
.subscribe(subscriber);
Edit
Related question:
chaining async method calls - javascript
With the help of Promises I could transform from synchronous to asynchronous calls:
foo(){
return new Promise(resolve=>{
baa().then(arg=>resolve(arg))
})
}
However, I need the other direction, something like:
pipe() {
var result = undefined;
await asyncCall(()=>{ //await is not allowed here; forces pipe to by async
result = 5;
});
return result;
}
As a work around, I extended the resulting promise with a subscribe proxy, calling my actual subscribe method:
pipe(...operators){
let observable = this;
let promise = new Promise(async (resolve) =>{
for (let operator of operators){
observable = await this._applyOperator(operator, observable);
}
resolve(observable);
});
promise.subscribe = (subscriber)=>{
promise.then(resultingObservable =>{
resultingObservable.subscribe(subscriber);
})
};
return promise;
}
If you know a better solution, please let me know.
Related
I'm trying to write a "mixing" for JavaScript classes (controllers, in my app) to automatically "await" for a given function to be resolved, before actually invoke the real methods. Real class methods should receive the resolved value as last argument.
Here is the code of useAwait, where i'm looking for the static class property awaits and wrapping the originalFunc into a new async one. I'm calling the new function passing the original arguments plus the asyncFn call result:
const useAwait = (controller, asyncFn) => {
controller.constructor.awaits.forEach(func => {
const originalFunc = controller[func];
controller[func] = async (...args) => {
return originalFunc.apply(
controller,
[...args, await asyncFn.call(controller)]
);
};
});
}
So when useAwait(ctrl, this.load) is invoked on an instance this class:
class Controller {
static awaits = ['foo', 'bar'];
promise;
constructor() {
useAwait(this, this.load);
}
async foo(e, resolved) {
return resolved;
}
bar(resolved) {
return resolved;
}
async load() {
if (!this.promise) {
this.promise = new Promise(resolve => setTimeout(() => {
resolve('Hello World!');
}, 3000));
}
return this.promise;
}
}
The problem: all seems fine for foo (already async), but it's not for bar: the result is a Promise because now the bar is wrapped in async (wan't before). I know that an async function result is wrapped into a Promise. Codepen example where bar call outputs "[object Promise]".
So the question is: in theory, I should check if the original function is async and if it was not, await for it's return value?
...in theory, I should check if the original function is async and if it was not, await for it's return value?"
It wouldn't matter, your wrapper is async; an async function always returns a promise, whether you use await or not. Moreover, your wrapper can't be synchronous, because it needs to call awaitFn (load, in the example) and wait for its result.
If you're going to wrap originalFunction (bar) such that it waits for awaitFn (load) to complete, the wrapped version of it needs to be asynchronous (either async, or return a promise explicitly [or accept a callback, but better IMHO to use promises]). It cannot be synchronous, because awaitFn (load) isn't synchronous.
If the class instance isn't ready for use when you construct it, you might consider using a static method to get an instance instead; the static instance would return a promise that it fulfills with the instance once load is complete. Rough sketch:
class Controller {
dataFromLoadingProcess;
constructor(dataFromLoadingProcess) {
this.dataFromLoadingProcess = dataFromLoadingProcess;
}
async foo(e, resolved) {
// ...optionally use `this.dataFromLoadingProcess`...
return resolved;
}
bar(resolved) {
// ...optionally use `this.dataFromLoadingProcess`...
return resolved;
}
static async createInstance() {
await /*...the loading process...*/;
return new Controller(/*...data from loading process here, perhaps...*/)
}
}
Js is not my first language, by a long chalk. I code in 5 or 6 others, and am comfortable with callbacks, which means that I should be able to grok promises.
However, I inherited a node.js project with something like this (simplified), which makes me uncertain)
let promise = someFunction.then({return xxxx;});
// lots of more code & promises
Promises.all().then(() => {return somethingElse});
I am comfortable with lots of promises and Promises.all() to wait for them all to resolve (although I am replacing all .then with await for cleaner code), BUT ...
that return inside a .then disturbs me. If I understand correctly, .then just wraps an async callback - which could happen at any time, even before the rest of the code and that Promises.all, leaving some of the code unexecuted.
Am I correct to be concerned, or is there something that I am missing?
Tl;dr - is it ok to return inside a .then?
Returning inside .then is extremely common and fine to do. What it does is it produces a Promise that now resolves to the value returned (rather than to the value returned by the previous Promise). Example:
// resolves to 1
const prom1 = Promise.resolve(1);
// chains off of prom1, resolves to 2
const prom2 = prom1.then(() => {
return 2;
});
prom1.then(console.log);
setTimeout(() => {
prom2.then(console.log);
});
This technique is very useful when you need to pass along a value from a prior .then to a subsequent .then.
const makeApiCall = num => Promise.resolve(num + 3);
Promise.resolve(1)
.then((result1) => {
return makeApiCall(result1);
})
.then((result2) => {
console.log(result2);
});
What the return will do in your situation specifically:
let promise = someFunction.then({return xxxx;});
// lots of more code & promises
Promises.all().then(() => {return somethingElse});
If promise is passed to the Promise.all, then its resolve value will be xxxx, rather than whatever someFunction resolves to. (If the Promise.all's .then ignores its argument, then it does nothing except wait for the returned value to resolve, if it's a Promise.)
const someFunction = () => Promise.resolve(1);
let promise = someFunction().then(() => {
return 'xxxx';
});
Promise.all([promise]).then((allResults) => {
console.log('allResults', allResults);
return 'somethingElse';
})
.then((result2) => {
console.log('Next `.then`', result2);
});
Note that your current code has a number of syntax issues you need to correct.
This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 1 year ago.
I am trying to figure out the best way to wait on the results of a subscription where I am passing in a callback. For example let's say I have the following:
function someMethod() {
let someResult = null;
someSubPub.subscribe(someTopic, someKey, someValue, (someResponse) => {
someResult = //do stuff in the callback with someResponse
});
return someResult;
}
I'm not quite sure how await would correctly work in this situation, aka blocking inside a callback.
Async/Await are nice, but sometimes it's simpler to think about the Promise construct that async/await abstracts away.
I suggest:
function someMethod() {
return new Promise((resolve, reject) => {
someSubPub.subscribe(someTopic, someKey, someValue, () => {
const someResult = //do stuff in the callback
resolve(someResult);
});
});
}
If you don't want to work with promises directly, you can wrap someSubPub.subscribe to return a promise
function someSubPubSubscribePromise(topic, key, value) {
return new Promise((resolve, reject) => {
someSubPub.subscribe(topic, key, value, resolve);
});
}
async function someMethod() {
await someSubPubSubscribePromise(someTopic, someKey, someValue);
const someResult = //do stuff in the callback
return someResult;
}
In either of those examples, you can do const result = await someMethod() (you can await both async methods and regular methods that return a promise)
One thing to consider: usually a pub/sub interface can call the callback multiple times. An async method / a method returning a promise can only resolve or reject exactly once. Probably you should be unsubscribing in the callback after you've responded to the event once?
I'd like to accomplish the following using promises: only execute further once the state of something is ready. I.e. like polling for an external state-change.
I've tried using promises and async-await but am not getting the desired outcome. What am I doing wrong here, and how do I fix it?
The MDN docs have something similar but their settimeout is called within the promise--that's not exactly what I'm looking for though.
I expect the console.log to show "This function is now good to go!" after 5 seconds, but instead execution seems to stop after calling await promiseForState();
var state = false;
function stateReady (){
state = true;
}
function promiseForState(){
var msg = "good to go!";
var promise = new Promise(function (resolve,reject){
if (state){
resolve(msg);
}
});
return promise;
}
async function waiting (intro){
var result = await promiseForState();
console.log(intro + result)
}
setTimeout(stateReady,5000);
waiting("This function is now ");
What you're doing wrong is the promise constructor executor function executes immediately when the promise is created, and then never again. At that point, state is false, so nothing happens.
Promises (and async/await) are not a replacement for polling. You still need to poll somewhere.
The good news: async functions make it easy to do conditional code with loops and promises.
But don't put code inside promise constructor executor functions, because of their poor error handling characteristics. They are meant to wrap legacy code.
Instead, try this:
var state = false;
function stateReady() {
state = true;
}
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
async function promiseForState() {
while (!state) {
await wait(1000);
}
return "good to go!";
}
async function waiting(intro) {
var result = await promiseForState();
console.log(intro + result)
}
setTimeout(stateReady,5000);
waiting("This function is now ");
Based on your comments that you are waiting for messages from a server it appears you are trying to solve an X/Y problem. I am therefore going to answer the question of "how do I wait for server messages" instead of waiting for global variable to change.
If your network API accepts a callback
Plenty of networking API such as XMLHttpRequest and node's Http.request() are callback based. If the API you are using is callback or event based then you can do something like this:
function myFunctionToFetchFromServer () {
// example is jQuery's ajax but it can easily be replaced with other API
return new Promise(function (resolve, reject) {
$.ajax('http://some.server/somewhere', {
success: resolve,
error: reject
});
});
}
async function waiting (intro){
var result = await myFunctionToFetchFromServer();
console.log(intro + result);
}
If your network API is promise based
If on the other hand you are using a more modern promise based networking API such as fetch() you can simply await the promise:
function myFunctionToFetchFromServer () {
return fetch('http://some.server/somewhere');
}
async function waiting (intro){
var result = await myFunctionToFetchFromServer();
console.log(intro + result);
}
Decoupling network access from your event handler
Note that the following are only my opinion but it is also the normal standard practice in the javascript community:
In either case above, once you have a promise it is possible to decouple your network API form your waiting() event handler. You just need to save the promise somewhere else. Evert's answer shows one way you can do this.
However, in my not-so-humble opinion, you should not do this. In projects of significant size this leads to difficulty in tracing the source of where the state change comes form. This is what we did in the 90s and early 2000s with javascript. We had a lot of events in our code like onChange and onReady or onData instead of callbacks passed as function parameters. The result was that sometimes it takes you a long time to figure out what code is triggering what event.
Callback parameters and promises forces the event generator to be in the same place in the code as the event consumer:
let this_variable_consumes_result_of_a_promise = await generate_a_promise();
this_function_generate_async_event((consume_async_result) => { /* ... */ });
From the wording of your question you seem to be wanting to do this instead;
..somewhere in your code:
this_function_generate_async_event(() => { set_global_state() });
..somewhere else in your code:
let this_variable_consumes_result_of_a_promise = await global_state();
I would consider this an anti-pattern.
Calling asynchronous functions in class constructors
This is not only an anti-pattern but an impossibility (as you've no doubt discovered when you find that you cannot return the asynchronous result).
There are however design patterns that can work around this. The following is an example of exposing a database connection that is created asynchronously:
class MyClass {
constructor () {
// constructor logic
}
db () {
if (this.connection) {
return Promise.resolve(this.connection);
}
else {
return new Promise (function (resolve, reject) {
createDbConnection(function (error, conn) {
if (error) {
reject(error);
}
else {
this.connection = conn; // cache the connection
resolve(this.connection);
}
});
});
}
}
}
Usage:
const myObj = new MyClass();
async function waiting (intro){
const db = await myObj.db();
db.doSomething(); // you can now use the database connection.
}
You can read more about asynchronous constructors from my answer to this other question: Async/Await Class Constructor
The way I would solve this, is as follows. I am not 100% certain this solves your problem, but the assumption here is that you have control over stateReady().
let state = false;
let stateResolver;
const statePromise = new Promise( (res, rej) => {
stateResolver = res;
});
function stateReady(){
state = true;
stateResolver();
}
async function promiseForState(){
await stateResolver();
const msg = "good to go!";
return msg;
}
async function waiting (intro){
const result = await promiseForState();
console.log(intro + result)
}
setTimeout(stateReady,5000);
waiting("This function is now ");
Some key points:
The way this is written currently is that the 'state' can only transition to true once. If you want to allow this to be fired many times, some of those const will need to be let and the promise needs to be re-created.
I created the promise once, globally and always return the same one because it's really just one event that every caller subscribes to.
I needed a stateResolver variable to lift the res argument out of the promise constructor into the global scope.
Here is an alternative using .requestAnimationFrame().
It provides a clean interface that is simple to understand.
var serverStuffComplete = false
// mock the server delay of 5 seconds
setTimeout(()=>serverStuffComplete = true, 5000);
// continue until serverStuffComplete is true
function waitForServer(now) {
if (serverStuffComplete) {
doSomethingElse();
} else {
// place this request on the next tick
requestAnimationFrame(waitForServer);
}
}
console.log("Waiting for server...");
// starts the process off
requestAnimationFrame(waitForServer);
//resolve the promise or whatever
function doSomethingElse() {
console.log('Done baby!');
}
I have a method of rest call using request module which is restRequest() which returns response as promise which is asynchronous method, I have to call this method recursively with different parameters after getting the each results and passing that result to same method.
Example code:
restRequest(url, "POST").then(function(response) {
restRequest(secondUrl, 'GET', response).then(function(response2) {
}):
});
will this works, or any other things are there to solve this one.
I would use the async library for this
Specifically the waterfall
Which would work like
async.waterfall([
function firstRequest(callback) {
restRequest(url, "POST").then(function(response) {
callback(null, response);
});
},
function secondRequest (data, callback) {
restRequest(secondUrl, 'GET', data).then(function(response2) {
callback();
});
}
], function (err, result) {
// Handle err or result
});
Sorry for formatting I'm on mobile.
You can read about how async.waterfall works from the link above.
Your method works but depending on how many requests you have you can end up with quite a deep callback hell
But since you are using promises you can just return your promise chain like
restRequest(url, "POST")
.then(function(resp) {
return restRequest(secondUrl, "GET", resp);
})
.then(function(resp) {
return restRequest(thirdUrl, "GET", resp);
});
.then(function(resp) {
// do whatever keep the chain going or whatever
})
.catch(function(error) {
// if any of the promises error it will immediately call here.
});
With promises you can return a new promise from within a .then and just keep the chain going infinitely.
I'm just biased for async as i think it really improves readability when used right.
you could do something like:
let requestParams = [
[url, 'POST'],
[secondUrl, 'GET'],
...
];
function callRecursive(response){
if(!requestParams.length) return Promise.resolve(response);
let params = requestParams.shift();
if(response) params.push(response);
return restRequest(...params).then(callRecursive);
}
callRecursive().then(successCallbk).catch(errCallBk);
You can supply one or more arguments to bind to your partially applied function.
restRequest(url,"POST").then(restRequest.bind(this,secondUrl, "GET"))
.then(restRequest.bind(this,thirdUrl, "GET"));
Since these are fired off in serial, what you really have is a simple chain of functions (some return promises, some might not) that can compose (or sequence, here) together, which I find to be a neat way to isolate out everything you want to happen and then combine behaviors as needed. It's still a Promise chain under the hood, but expressed as a series. First, a few utility methods to help:
var curry = (f, ...args) =>
(f.length <= args.length) ? f(...args) : (...more) => curry(f, ...args, ...more);
var pipeP = (...fnlist) =>
acc => fnlist.reduce( (acc,fn) => acc.then(fn), Promise.resolve(acc));
then
//make restRequest only return a Promise once it's given its 3rd argument
var restRequest = autocurry(restRequest);
//define what our requests look like
var request1 = restRequest('firstUrl', "POST");//-> curried function, not yet called
var request2 = restRequest('secondUrl', 'GET');//-> curried function, not yet called
//define some simple methods to process responses
var extractURL = x => x.url;//-> simple function
var extractData = x=> x.data;//-> simple function
//final behaviors, i.e. do something with data or handle errors
//var handleData = ... //-> do something with "data"
//var handleError = ... //-> handle errors
//now, create a sort of lazy program chain waiting for a starting value
//that value is passed to request1 as its 3rd arg, starting things off
var handleARequest = pipeP(request1, extractURL, request2, extractData);
//and execute it as needed by passing it a starting request
handleARequest({postdata:5}).then(handleData).catch(handleErrors);
Recursion is the most obvious approach but it's not necessary. An alternative is to build a .then() chain by reducing an array of known parameters (urls and methods).
The process is presented here under "The Collection Kerfuffle".
function asyncSequence(params) {
return params.reduce(function(promise, paramObj) {
return promise.then(function(response) {
return restRequest(paramObj.url, paramObj.method, response);
});
}, Promise.resolve(null)); // a promise resolved with the value to appear as `response` in the first iteration of the reduction.
}
This will cater for any number of requests, as determined by the length of the params array.
Call as follows :
var params = [
{url:'path/1', method:'POST'},
{url:'path/2', method:'GET'},
{url:'path/3', method:'POST'}
];
asyncSequence(params).then(function(lastResponse) {
//all successfully completed
}).catch(function(e) {
// something went wrong
});