Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question about child.call and returned value #94

Open
paulcookie opened this issue Jul 26, 2018 · 4 comments
Open

Question about child.call and returned value #94

paulcookie opened this issue Jul 26, 2018 · 4 comments

Comments

@paulcookie
Copy link

Is there some limitations/caveats for child.call returned value as promise, like it's done at child.get functionality? Because if I need to pass some arguments to child model functions, then I need to switch from promise based code to event driven style.. but I wanna use just one of it

@yowainwright
Copy link
Contributor

@paulcookie sorry it took me so long to respond. 😞

Could you provide a code snippet? ...so I can more clearly see what you want/wanted to do. It would also be cool to see what you came up with!

@paulcookie
Copy link
Author

paulcookie commented Oct 9, 2018

wow, thanks for reply, even if it took a long time..

Postmate could do things like this:
child.get('childMethod').then(returnedValue => {...})

but if we need to pass some arguments to child method and get returned promise we can't do this easily, we should use event emitter:

// parent

child.on('childMethodSuccess', someData => { ... })
// we should subscribe to event before call to get returned data

child.call('childMethod', args) // and after we could call child with args

// child model
{
  childMethod: (args) => {
    // some async logic

    parent.emit('childMethodSuccess', returnedValue)  // instead of return we should emit custom event with our data
  }
}

so basically I wanna do things like this:

// parent

child.call('someChildMethod', args).then(someData => { ... }) // call and get Promise as returned value

// child model

{
  childMethod: (args) => new Promise((resolve, reject) => {
    // some async logic

    resolve(returnedValue) // just resolve promise with some data
  }
}

in this case we don't need to create and name custom events

@mrcoles
Copy link

mrcoles commented Mar 9, 2019

@yowainwright is there a reason why the 'call' method does not return a Promise in the same way that the 'get' method does? This could be useful if you want to both (1) pass data across and (2) validate or do something with the response you’re given.

Looking at the code it seems like things could be simplified so 'call' and 'get' use the same API, e.g., if it passed any data through and then resolveValue were updated to take a third argument that uses the data iff it’s a function:

/**
 * Takes a model, and searches for a value by the property
 * @param  {Object} model     The dictionary to search against
 * @param  {String} property  A path within a dictionary (i.e. 'window.location.href')
 * @param  {Object} data      Additional information from the get request that is
 *                            passed to functions in the child model
 * @return {Promise}
 */
export const resolveValue = (model, property, data) => {
  const unwrappedContext = typeof model[property] === 'function'
    ? model[property](data) : model[property]
  return Postmate.Promise.resolve(unwrappedContext)
}

(NOTE: strangely data is already listed as a param of this function in the code!)

EDIT: I’m seeing in the test code an example that does a call, immediately followed by a get, which seems like it could work, but it still seems odd to me that we can’t just get a response from 'call'? https://github.com/dollarshaveclub/postmate/blob/master/test/acceptance/test.js#L50

@tw00
Copy link

tw00 commented Sep 20, 2019

Just stumbled upon this as well. It seems a little bit strange, that call wouldn't return a promise like get. @yowainwright is there any technical limitation that we're not aware of? I think this is a great library, so it would be really nice if it would support this use case.

However, here is my solution to the problem for now (based on @mrcoles observation):

// Save to postmate-async-func.js

/* Creates a function property that takes arguments and returns a promise */
export function makeAsyncFunctionProperty(func) {
  let cachedArgs = null;
  return (args) => {
    if ( args !== undefined ) {
      cachedArgs = args;
    } else {
      return func(cachedArgs).catch(e => new Error(e)); // Does not inclued stack trace
    }
  }
}

/* Enhances ParentAPI with new method "callAsync" */
export function enhanceChildAsyncCall(child) {
  child.callAsync = (fname, data) => {
    child.call(fname, data || null);
    return Promise.resolve(child.get(fname)).then(res => {
      if ( res instanceof Error  ) {
        throw res;
      }
      return res;
    });
  };
  return child;
}

The two functions can then be used like this to create async functions that take arguments:

// Your model
import { makeAsyncFunctionProperty } from './postmate-async-func'

const handshake = new Postmate.Model({
 
  asyncMult: makeAsyncFunctionProperty((value) => {
    let result = value * 5;
    return new Promise((resolve) => {
      setTimeout(() => resolve(result), 1000)
    })
  }),

  asyncAdd: makeAsyncFunctionProperty((value) => {
    let result = value + 5;
    return new Promise((resolve) => {
      setTimeout(() => resolve(result), 1000)
    })
  }),

  asyncFail: makeAsyncFunctionProperty((value) => {
    return Promise.reject()
  }),
}
// Your parent
import { enhanceChildAsyncCall } from './postmate-async-func'

handshake.then(child => {
  enhanceChildAsyncCall(child);

  child.callAsync('asyncMult', 4).then(res => {
    console.log("Got result", res)
  });

  child.callAsync('asyncAdd', 3).then(res => {
    console.log("Got result", res)
  });

  child.callAsync('asyncFail').catch(e => {
    console.log("Got error", e)
  });
})

This also takes care of rejected promises.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants