构造函数中的异步操作

时间:2018-04-18 16:58:58

标签: javascript promise prototype

嘿,我对功能的原型和内在性有疑问。你能解释一下我如何从构造函数返回arr并将这个arr添加到原型中吗?

getArray

Service and getService()中,this.arr未定义。 class Tab extends Component { static Title = ({ children, handleTabClick, tabId }) => { return <div onClick={() => handleTabClick(tabId)}>{children}</div>; }; static Content = ({ children, tabId, activeTab }) => { console.log(tabId, "a", activeTab); return tabId === activeTab ? children : ""; }; render() { return React.Children.map(this.props.children, child => React.cloneElement(child, { handleTabClick: this.props.handleTabClick, tabId: this.props.id, activeTab: this.props.activeTab }) ); } } class Tabs extends React.Component { state = { activeTab: this.props.activeTab }; handleTabClick = id => { this.setState({ activeTab: id }); }; render() { const { children, activeTab } = this.props; return React.Children.map(children, child => React.cloneElement(child, { activeTab: this.state.activeTab, handleTabClick: this.handleTabClick }) ); } } const App = () => { return ( <Tabs activeTab={1}> <Tab id={1}> <Tab.Title>Tab 1</Tab.Title> <Tab.Content>Tab 1 content goes here</Tab.Content> </Tab> <Tab id={2}> <Tab.Title>Tab 2</Tab.Title> <Tab.Content>Tab 2 content goes here</Tab.Content> </Tab> </Tabs> ); }; 是角度工厂和前端和后端之间的连接

1 个答案:

答案 0 :(得分:4)

It is particularly difficult to put asynchronous operations in a constructor. This is for several reasons:

  1. The constructor needs to return the newly created object so it can't return a promise that would tell you when the async operation is done.
  2. If you do an asynchronous operation inside the constructor that sets some instance data and the constructor returns the object, then you have no way for the calling code to know when the async operation is actually done.

For these reasons, you usually don't want to do an async operation inside a constructor. These are some of the various options for dealing with that issue:

Break async object initialization into a separate method that can return a promise

class MyObj() {
   constructor(someValue) {
       this.someProp = someValue;
   }
   init() {
       return Service.getService().then(val => {
          this.asyncProp = val;
       });
   }
}

let x = new MyObj(someVal);
x.init().then(() => {
    // ready to use x here
}).catch(err => {
    // handle error
});

Use Factory Function that Returns a Promise

This combines the previous method with a factory function that does some of the more common work for you. It also doesn't reveal the new object until its fully initialized which is a good programming practice.

class MyObj() {
   constructor(someValue) {
       this.someProp = someValue;
   }
   init() {
       return Service.getService().then(val => {
          this.asyncProp = val;
       });
   }
}

function createMyObj(someValue) {
    let x = new MyObj(someVal);
    return x.init().then(() => {
        // make the new object be the resolved value of the promise
        return x;
    });
}

createMyObj(someVal).then(obj => {
    // obj ready to use and fully initialized here
}).catch(err => {
    // handle error here
});

If you're using modules, you can export only the factory function (no need to export the class itself) and thus enforce that the object is initialized properly and not used until that initialization is done.

Use Events to Signal Completion

This scheme is used in a lot of I/O related APIs. The general idea is that you return an object from the constructor, but the caller knows that object hasn't really completed its initialization until a particular event occurs.

// object inherits from EventEmitter
class MyObj extends EventEmitter () {
   constructor(someValue) {
       this.someProp = someValue;

       Service.getService().then(val => {
          this.asyncProp = val;
          // signal to caller that object has finished initializing
          this.emit('init', val);
       });
   }
}

let x = new MyObj(someVal);
x.on('init', () => {
    // object is fully initialized now
}).on('error', () => {
    // some error occurred
});

Hackish way to put the Async Operation in the Constructor

Though I wouldn't recommend using this technique, this is what it would take to put the async operation in the actual constructor itself:

class MyObj() {
   constructor(someValue) {
       this.someProp = someValue;
       this.initPromise = Service.getService().then(val => {
          this.asyncProp = val;
       });
   }
}

let x = new MyObj(someVal);
x.initPromise.then(() => {
   // object ready to use now
}).catch(err => {
   // error here
});

Note, you see the first design pattern in many places in various APIs. For example, for a socket connection in node.js, you would see this:

let socket = new net.Socket(...);
socket.connect(port, host, listenerCallback);

The socket is created in the first step, but then connected to something in the second step. And, then the same library has a factory function net.createConnection() which combines those two steps into one function (an illustration of the second design pattern above). The net module examples don't happen to use promises (very few nodejs original apis do), but they accomplish the same logic using callbacks and events.


Other note on your code

You likely also have an issue with the value of this in your code. A .then() handler does not naturally preserve the value of this from the surrounding environment if you pass it a regular function() {} reference. So, in this:

function Constructor(){
   Service.getService().then(function(data){
      this.arr = data.data.array;
      return this.arr
   })
}

The value of this when you try to do this.arr = data.data.array; is not going to be correct. The simplest way to fix that issue in ES6 is to use a fat arrow function instead:

function Constructor(){
   Service.getService().then(data => {
      this.arr = data.data.array;
      return this.arr
   });
}