简体   繁体   English

在 JavaScript 中扩展 Promise

[英]Extending a Promise in javascript

I'm learning about classes and inheritance in javascript.我正在学习 javascript 中的类和继承。 I thought that the following is a fairly standard way of extending an existing object as I got the style from the MDN docs on Object.create我认为以下是扩展现有对象的相当标准的方法,因为我从Object.createMDN 文档中获得了样式

I was expecting to see 'ok' and then 'Yay!我期待看到“好”然后“耶! Hello' in the console, but instead I go this error:你好'在控制台中,但我却出现了这个错误:

Uncaught TypeError: #<MyPromise> is not a promise
at new MyPromise (<anonymous>:5:17)
at <anonymous>:19:6

It looks like the Promise constructor is throwing an exception because it can tell that the object I've given it to initialise isn't a straightforward Promise.看起来 Promise 构造函数正在抛出异常,因为它可以告诉我给它初始化的对象不是一个简单的 Promise。

I want the Promise constructor to initialise my object as if it was a Promise object, so I can then extend the class.我希望 Promise 构造函数将我的对象初始化为 Promise 对象,这样我就可以扩展该类。 Why wouldn't they write the Promise constructor to work with this common pattern?他们为什么不编写 Promise 构造函数来使用这种常见模式? Am I doing something wrong?难道我做错了什么? Cheers for taking a look!为观看干杯!

MyPromise = function(message, ok) {
    var myPromise = this;
    this.message = message;
    this.ok = ok;
    Promise.call(this, function(resolve, reject) {
        if(this.ok) {
            console.log('ok');
            resolve(myPromise.message);
        } else {
            console.log('not ok');
            reject(myPromise.message);
        }   
    }); 
};  

MyPromise.prototype = Object.create(Promise.prototype);
MyPromise.prototype.constructor = MyPromise;

(new MyPromise('Hello', true))
    .then(function(response) {console.log('Yay! ' + response);})
    .except(function(error) {console.log('Aww! ' + error);});

I was originally trying to make a BatchAjax class that you could use like:我最初试图制作一个您可以使用的 BatchAjax 类,例如:

(new BatchAjax([query1, query2]))
    .then(function(response) {console.log('Fires when all queries are complete.');}); 

It was just a bit of fun really.真的只是有点乐趣。

The native Promise class (like Error and Array ) cannot be correctly subclassed with the old ES5-style mechanism for subclassing.原生的Promise类(如ErrorArray )不能用旧的 ES5 风格的子类化机制正确地子类化。

The correct way to subclass Promise is through class syntax:子类化Promise的正确方法是通过class语法:

class MyPromise extends Promise {
}

Example:例子:

 class MyPromise extends Promise { myMethod() { return this.then(str => str.toUpperCase()); } } // Usage example 1 MyPromise.resolve("it works") .myMethod() .then(result => console.log(result)) .catch(error => console.error(error)); // Usage example 2 new MyPromise((resolve, reject) => { if (Math.random() < 0.5) { resolve("it works"); } else { reject(new Error("promise rejected; it does this half the time just to show that part working")); } }) .myMethod() .then(result => console.log(result)) .catch(error => console.error(error));


If it's your goal to do that without class , using mostly ES5-level features, you can via Reflect.construct .如果你的目标是在没有class情况下做到这一点,主要使用 ES5 级别的特性,你可以通过Reflect.construct Note that Reflect.construct is an ES2015 feature, like class , but you seem to prefer the ES5 style of creating classes.注意Reflect.constructReflect.construct的一个特性,就像class ,但你似乎更喜欢 ES5 风格的创建类。

Here's how you do that:这是你如何做到的:

// Create a constructor that uses `Promise` as its super and does the `super` call
// via `Reflect.construct`
const MyPromise = function(executor) {
    return Reflect.construct(Promise, [executor], MyPromise);
};
// Make `MyPromise` inherit statics from `Promise`
Object.setPrototypeOf(MyPromise, Promise);
// Create the prototype, add methods to it
MyPromise.prototype = Object.create(Promise.prototype);
MyPromise.prototype.constructor = MyPromise;
MyPromise.prototype.myMethod = function() {
    return this.then(str => str.toUpperCase());
};

Then use it just like Promise :然后像Promise一样使用它:

MyPromise.resolve("it works")
    .myMethod()
    .then(result => console.log(result))
    .catch(error => console.error(error));

or或者

new MyPromise(resolve => resolve("it works"))
    .myMethod()
    .then(result => console.log(result))
    .catch(error => console.error(error));

etc.等等。

Live Example:现场示例:

 // Create a constructor that uses `Promise` as its super and does the `super` call // via `Reflect.construct` const MyPromise = function(executor) { return Reflect.construct(Promise, [executor], MyPromise); }; // Make `MyPromise` inherit statics from `Promise` Object.setPrototypeOf(MyPromise, Promise); // Create the prototype, add methods to it MyPromise.prototype = Object.create(Promise.prototype); MyPromise.prototype.constructor = MyPromise; MyPromise.prototype.myMethod = function() { return this.then(str => str.toUpperCase()); }; // Usage example 1 MyPromise.resolve("it works") .myMethod() .then(result => console.log(result)) .catch(error => console.error(error)); // Usage example 2 new MyPromise((resolve, reject) => { if (Math.random() < 0.5) { resolve("it works"); } else { reject(new Error("promise rejected; it does this half the time just to show that part working")); } }) .myMethod() .then(result => console.log(result)) .catch(error => console.error(error));


If you want to avoid changing the prototype of MyPromise , you can copy the static properties over, but it's not quite the same thing:如果您想避免更改MyPromise的原型,您可以将静态属性复制过来,但这并不完全相同:

// Create a constructor that uses `Promise` as its super and does the `super` call
// via `Reflect.construct`
const MyPromise = function(executor) {
    return Reflect.construct(Promise, [executor], MyPromise);
};
// Assign the statics (`resolve`, `reject`, etc.) to the new constructor
Object.assign(
    MyPromise,
    Object.fromEntries(
        Reflect.ownKeys(Promise)
            .filter(key => key !== "length" && key !== "name")
            .map(key => [key, Promise[key]])
    )
);
// Create the prototype, add methods to it
MyPromise.prototype = Object.create(Promise.prototype);
MyPromise.prototype.constructor = MyPromise;
MyPromise.prototype.myMethod = function() {
    return this.then(str => str.toUpperCase());
};

Using it is the same, of course.使用它当然是一样的。

Live Example:现场示例:

 // Create a constructor that uses `Promise` as its super and does the `super` call // via `Reflect.construct` const MyPromise = function(executor) { return Reflect.construct(Promise, [executor], MyPromise); }; // Assign the statics (`resolve`, `reject`, etc.) to the new constructor Object.assign( MyPromise, Object.fromEntries( Reflect.ownKeys(Promise) .filter(key => key !== "length" && key !== "name") .map(key => [key, Promise[key]]) ) ); // Create the prototype, add methods to it MyPromise.prototype = Object.create(Promise.prototype); MyPromise.prototype.constructor = MyPromise; MyPromise.prototype.myMethod = function() { return this.then(str => str.toUpperCase()); }; // Usage example 1 MyPromise.resolve("it works") .myMethod() .then(result => console.log(result)) .catch(error => console.error(error)); // Usage example 2 new MyPromise((resolve, reject) => { if (Math.random() < 0.5) { resolve("it works"); } else { reject(new Error("promise rejected; it does this half the time just to show that part working")); } }) .myMethod() .then(result => console.log(result)) .catch(error => console.error(error));

My latest solution is to compose a Promise object into my class as this.promise and then pretend to be inheriting from Promise by overriding all the instance methods of Promise and passing them on to the this.promise object.我最新的解决方案是将一个 Promise 对象作为 this.promise 组合到我的类中,然后通过覆盖 Promise 的所有实例方法并将它们传递给 this.promise 对象来假装是从 Promise 继承的。 Hilarity ensues.欢闹随之而来。 I'd really welcome people pointing out the drawbacks to this approach.我真的很欢迎人们指出这种方法的缺点。

Nothing is too obvious for me to have missed.对我来说,没有什么是显而易见的。

When I paste this code into the Chrome console, it seems to work.当我将此代码粘贴到 Chrome 控制台时,它似乎可以工作。 That's as far as I comprehend.我理解的就这么多了。

Cheers for taking a look.为看一眼干杯。

BatchAjax = function(queries) {
    var batchAjax = this;
    this.queries = queries;
    this.responses = [];
    this.errorCount = 0;
    this.promise = new Promise(function(resolve, reject) {
        batchAjax.executor(resolve, reject);
    });
};
BatchAjax.prototype = Object.create(Promise.prototype);
BatchAjax.prototype.constructor = BatchAjax;
BatchAjax.prototype.catch = function(fail) {
    return this.promise.catch(fail);
}
BatchAjax.prototype.then = function(success, fail) {
    return this.promise.then(success, fail);
};
BatchAjax.prototype.executor = function(resolve, reject) {
    var batchAjax = this;
    $.each(this.queries, function(index) {
        var query = this;
        query.success = function (result) {
            batchAjax.processResult(result, index, resolve, reject);
        };
        query.error = function (jqXhr, textStatus, errorThrown) {
            batchAjax.errorCount++;
            var result = {jqXhr: jqXhr, textStatus: textStatus, errorThrown: errorThrown};
            batchAjax.processResult(result, index, resolve, reject);
        };
        $.ajax(query);
    });
};
BatchAjax.prototype.processResult = function(result, index, resolve, reject) {
    this.responses[index] = result;
    if (this.responses.length === this.queries.length) {
        if (this.errorCount === 0) {
            resolve(this.responses);
        } else {
            reject(this.responses);
        }
    }
};

// Usage
var baseUrl = 'https://jsonplaceholder.typicode.com';
(new BatchAjax([{url: baseUrl + '/todos/4'}, {url: baseUrl + '/todos/5'}]))
    .then(function(response) {console.log('Yay! ', response);})
    .catch(function(error) {console.log('Aww! ', error);});

As @tobuslieven's answer suggested, it's not necessary to extend Promise.正如@tobuslieven 的回答所建议的那样,没有必要扩展 Promise。 Below is a simplified sample.下面是一个简化的示例。

ES6: ES6:

class MyPromise {

  async executor () {
    if(!this.name) {
      throw new Error('whoops!')
    }
    console.log('Hello', this.name)
  }

  then () {
    const promise = this.executor()
    return promise.then.apply(promise, arguments)
  }

  catch () {
    const promise = this.executor()
    return promise.catch.apply(promise, arguments)
  }
}

ES5: ES5:

function MyPromise () { }

MyPromise.prototype.executor = function () {
  var self = this
  return new Promise(function (resolve, reject) {
    if (!self.name) {
      return reject(new Error('whoops!'))
    }
    console.log('Hello', self.name)
    resolve()
  })
}

MyPromise.prototype.then = function () {
  var promise = this.executor()
  return promise.then.apply(promise, arguments)
}

MyPromise.prototype.catch = function () {
  var promise = this.executor()
  return promise.catch.apply(promise, arguments)
}

both can be tested by:两者都可以通过以下方式进行测试:

var promise = new MyPromise()
promise.name = 'stackoverflow'
promise
  .then(function () {
    console.log('done!')
  })
  .catch(console.log)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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