0

I have written a util class, which is a wrapper around promise based vendor library. My application will call this class (within App & Actions) to perform certain operations.

I am currently dealing with a scenario where my code needs to know the return value of promise within the Util class before proceeding to the next step. How can I do that?

The following is the tree structure of my code:

├── index.html
├── package-lock.json
├── package.json
├── scripts
│   ├── vendor_script.min.js
├── src
│   ├── actions
│   │   └── index.js
│   ├── common
│   │   └── js
│   │       └── WrapperAroundVendorScript_Utils.js
│   ├── components
│   │   └── app.js
│   ├── index.js
│   └── reducers
│       └── index.js
├── style
│   └── style.css
├── webpack.config.js
├── yarn-error.log
└── yarn.lock

Here, vendor_script.min.js is a vendor supplied JS library that uses JS promises to perform various actions. I have written a util class (WrapperAroundVendorScript_Utils.js) to abstract out the implementation details of vendor library.

WrapperAroundVendorScript_Utils.js

let instance = null;

class VendorUtils {

  constructor (){

    const config = {
      some: value,
    }

    this._vendor = typeof window !== 'undefined' && window.vendor ? window.vendor : require([PATH]);
    this._vendor.config = config;
  };

  static getInstance() {
    if(typeof instance == "undefined" || !instance){
      instance = new VendorUtils();
    }
    return instance;
  }

  doSomething(param1, param2) {
    this._vendor.domSomething(param1 + param2);
      .then(retVal => {
        return {retVal};
      });
  };

  doSomethingElse(param1, param2) {
    this._vendor.domSomethingElse(param1 + param2);
      .then(retVal => {
        return {retVal};
      });
  };
}

module.exports.VendorUtils = VendorUtils;

app.js

import React, { Component } from 'react';
import {VendorUtils} from '../common/js/VendorUtils';

export default class App extends Component {

  clicked(){
    let utilsInstance = VendorUtils.getInstance();
    let x = utilsInstance.doSomething('a','b');
    let y = utilsInstance.doSomethingElse(x,'a');
    // call action with values received in previous steps
  }

  render() {
    return (
      <div>
        <button type='button' onClick={this.clicked}>Click Me!</button>
        <button type='button' onClick={this.clicked}>Click Me!</button>
        <button type='button' onClick={this.clicked}>Click Me!</button>
      </div>
    );
  }
}

PS: I need such synchronous behavior because there are multiple child/nested/sub components within the class that will be calling such common functions.

7
  • .then not working for you? Commented Nov 11, 2017 at 9:04
  • @Sag1v .then would create a promise that would evaluate whenever it gets to execute but by that time the calling function has already moved on to the next line of code. So, value of x would be undefined. Commented Nov 11, 2017 at 9:09
  • 1
    the next line / lines that needs the value should be inside the .then block. you could use async / await but thats just promises and generators behind the scene. Commented Nov 11, 2017 at 9:26
  • @RahilParikh It's impossible to do without creating a chain of promises. You cannot block until a promise resolves, you can only put all the code that should execute later in a then callback (or after an await) Commented Nov 11, 2017 at 9:51
  • 1
    @RahilParikh: The thing you're missing is, as soon as your function does some asynchronous work with promises, then anything that uses that function also must be asynchronous. You can't mix sync and async Commented Nov 11, 2017 at 10:29

3 Answers 3

2

This is a perfect example of where you would want to use async/await

async clicked() {
  const utilsInstance = VendorUtils.getInstance();
  const x = await utilsInstance.doSomething('a','b');
  const y = await utilsInstance.doSomethingElse(x,'a');
  ...
}
Sign up to request clarification or add additional context in comments.

6 Comments

I tried the solution that you suggested but when I put console.log statements in clicked and doSomething, I noticed that JavaScript first prints value of x from clicked (which is undefined) and then prints the returns the value retVal from doSomething (which is ab). Please note that the code within .then is a long running task that can sometimes take upto 4/5 seconds.
@RahilParikh you get undefined because you still missing the most important point of the problem, you are awaiting of undefined, because you don't return promise in your do something functions, please see my answer.
@RahilParikh as mentioned by Nik, you don't appear to be returning a promise from inside your doSomething functions. Fix those calls and you'll find this code works as expected.
This, by itself, is not enough. The OP has to fix their doSomething() and doSomethingElse() methods to work properly too.
@jfriend00 already pointed that out in the comments, the question was more about how to use the result of one promise in a subsequent one.
|
0

You can either execute the code you want to wait inside the then clause of you promoise, or you could use the async / await feature of Javascript.

A detailed guide with examples can be found here.

Comments

0

There are some problems with your doSomething functions:

doSomething(param1, param2) {
  this._vendor.domSomething(param1 + param2);
    .then(retVal => {
      return {retVal};
    });
};
  1. There should be no semicolon ";" before ".then" method.
  2. Your do something functions should return promises, you can not return a value from a "then" block.

Consider to change your do something functions a bit:

doSomething(param1, param2) {
  // return the promise
  return this._vendor.domSomething(param1 + param2)
    // maybe you don't need this part at all if you want to get only the value instead of {retVal: value} object
    .then(retVal => {
      // if you do async operations here make sure you returned a new
      //   promise here instead of object, in that case you would need
      //   another .then block to process internal promise that you 
      //   would create inside this .then block 
      return {retVal};
    });
};

that way, inside your async "clicked" function you would be able to use await for returned promises:

async clicked() {
  let utilsInstance = VendorUtils.getInstance();

  // you can check what doSomething returns here - it would be a promise
  let doSomethingPromise = utilsInstance.doSomething('a','b');

  // await a previously saved promise to get a value
  let x = await doSomethingPromise;

  // obviously you still can await promise in one sentence
  let y = await utilsInstance.doSomethingElse(x,'a');

  // call action with values received in previous steps
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.