Extending a Promise in javascript

旧时模样 提交于 2019-12-30 09:14:06

问题


I'm learning about classes and inheritance in 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

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.

I want the Promise constructor to initialise my object as if it was a Promise object, so I can then extend the class. Why wouldn't they write the Promise constructor to work with this common pattern? 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:

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

It was just a bit of fun really.


回答1:


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



回答2:


The native Promise class (like Error and Array) cannot be correctly subclassed with the old ES5-style mechanism for subclassing.

The correct way to subclass Promise is through class syntax:

class MyPromise extends Promise {
}

Example:

class MyPromise extends Promise {
  fail(...args) {
    return this.catch(...args);
  }
}

const p = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject("error here");
  }, 0);
});

p.fail(error => {
  console.log("failed: " + error);
});

You can probably also do it with a mostly ES5-style plus Reflect.construct, but if you have Reflect.construct, you have class, so... Actually, I'm not immediately having any luck doing so, not least because you can only call it with a class constructor, so you'd still have to use class...



来源:https://stackoverflow.com/questions/41792036/extending-a-promise-in-javascript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!