构造函数中的异步操作

Mat*_*Now 3 javascript prototype promise

嘿,我对函数的原型和固有性有疑问。您能否解释一下如何从构造函数返回arr并将此arr添加到原型中?

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

Constructor.prototype.getArray = function(){
   console.log(this.arr)
})
example.getArray();
Run Code Online (Sandbox Code Playgroud)

并且在getArraythis.arr中是未定义的。Service and getService()有角度的工厂以及前端和后端之间的连接

jfr*_*d00 5

将异步操作放在构造函数中特别困难。这有几个原因:

  1. 构造函数需要返回新创建的对象,因此它不能返回将告诉您异步操作何时完成的承诺。
  2. 如果在构造函数内部进行异步操作以设置一些实例数据,并且构造函数返回对象,那么调用代码将无法知道异步操作何时真正完成。

由于这些原因,您通常不希望在构造函数中执行异步操作。这些是处理该问题的各种选项中的一些:

将异步对象初始化分为可以返回承诺的单独方法

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
});
Run Code Online (Sandbox Code Playgroud)

使用返回承诺的工厂函数

这将以前的方法与工厂函数结合在一起,该函数可以为您完成一些更常见的工作。在完全初始化之前,它也不会显示新对象,这是一种很好的编程习惯。

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
});
Run Code Online (Sandbox Code Playgroud)

如果使用模块,则只能导出工厂函数(无需导出类本身),从而强制执行对象正确初始化,并且在完成初始化之前不使用该对象。

使用事件表示完成

许多I / O相关的API中都使用了该方案。一般的想法是,您从构造函数中返回一个对象,但是调用者知道该对象直到某个特定事件发生时才真正完成其初始化。

// 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
});
Run Code Online (Sandbox Code Playgroud)

将异步操作放入构造函数的骇人方式

尽管我不建议您使用此技术,但这是将异步操作放入实际构造函数本身的过程:

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
});
Run Code Online (Sandbox Code Playgroud)

注意,您会在各种API的许多地方看到第一个设计模式。例如,对于node.js中的套接字连接,您将看到以下内容:

let socket = new net.Socket(...);
socket.connect(port, host, listenerCallback);
Run Code Online (Sandbox Code Playgroud)

套接字是在第一步中创建的,但是在第二步中已连接到某些对象。而且,同一个库具有工厂功能net.createConnection(),该功能将这两个步骤组合为一个功能(上面第二种设计模式的说明)。该net模块实例并不碰巧使用诺言(很少有原创的NodeJS的API做),但它们完成使用回调和事件相同的逻辑。


有关代码的其他说明

this的代码中的值可能也有问题。一个.then()处理程序不自然保护的价值this从周围环境中如果你传递一个普通function() {}的参考。因此,在此:

function Constructor(){
   Service.getService().then(function(data){
      this.arr = data.data.array;
      return this.arr
   })
}
Run Code Online (Sandbox Code Playgroud)

this当您尝试做时的值将this.arr = data.data.array;是不正确的。解决ES6中该问题的最简单方法是改为使用粗箭头功能:

function Constructor(){
   Service.getService().then(data => {
      this.arr = data.data.array;
      return this.arr
   });
}
Run Code Online (Sandbox Code Playgroud)