简体   繁体   中英

How to wait for javascript promise to return value before continuing execution?

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.

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');
  ...
}

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 .

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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM