在JavaScript中扩展Promise

tob*_*ven 5 javascript oop inheritance class promise

我正在学习javascript中的类和继承。我认为以下是扩展现有对象的相当标准的方法,因为我从Object.createMDN文档中获得了样式

我期待看到“确定”,然后看到“是!控制台中的“ Hello”,但出现此错误:

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

看起来Promise构造函数正在抛出异常,因为它可以告诉我给我初始化的对象不是一个简单的Promise。

我希望Promise构造函数初始化我的对象,就像它是一个Promise对象一样,这样我就可以扩展该类。他们为什么不编写Promise构造函数来使用这种通用模式?难道我做错了什么?为您加油!

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

我最初试图制作一个可以使用的BatchAjax类,例如:

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

真的有点有趣。

T.J*_*der 7

本机Promise类(如ErrorArray)不能用旧的 ES5 风格的子类化机制正确地子类化。

子类化的正确方法Promise是通过class语法:

class MyPromise extends Promise {
}
Run Code Online (Sandbox Code Playgroud)

例子:

class MyPromise extends Promise {
}
Run Code Online (Sandbox Code Playgroud)


如果您的目标是不class使用Reflect.construct. 请注意,这Reflect.construct是一个 ES2015 功能,例如class,但您似乎更喜欢 ES5 风格的创建类。

这是你如何做到的:

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

然后像这样使用它Promise

MyPromise.resolve("it works")
    .myMethod()
    .then(result => console.log(result))
    .catch(error => console.error(error));
Run Code Online (Sandbox Code Playgroud)

或者

new MyPromise(resolve => resolve("it works"))
    .myMethod()
    .then(result => console.log(result))
    .catch(error => console.error(error));
Run Code Online (Sandbox Code Playgroud)

等等。

现场示例:

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


如果您想避免更改 的原型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());
};
Run Code Online (Sandbox Code Playgroud)

使用它当然是一样的。

现场示例:

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

  • @tobuslieven 扩展类的“​​原始语法”从不支持内置构造函数,也不适合这样做;这就是 ES6 为此使用新方法的原因。 (3认同)
  • @tobuslieven:如果不破坏更改,您提出的建议是不可能的。在不破坏大量现有代码的情况下推进 JavaScript 是**极具挑战性的**,TC39 非常重视这一挑战。重新“两只狗”——旧语法是一种我们谢天谢地不再需要使用的技巧。没有必要继续下去。 (2认同)
  • 当我尝试调用 MyPromise.then 时,浏览器会抛出错误“Uncaught TypeError:Promise 解析或拒绝函数无法在 Promise.then (&lt;anonymous&gt;) 处调用” (2认同)

tob*_*ven 5

我最新的解决方案是将一个 Promise 对象作为 this.promise 组合到我的类中,然后通过覆盖 Promise 的所有实例方法并将它们传递给 this.promise 对象来假装是从 Promise 继承的。欢闹随之而来。我真的很欢迎人们指出这种方法的缺点。

对我来说,没有什么是显而易见的。

当我将此代码粘贴到 Chrome 控制台时,它似乎可以工作。我理解的就这么多。

为看一眼干杯。

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

  • 在我看来,这个答案是正确的答案。这是我发现的唯一一种支持 a) 调用超级构造函数并访问 `resolve` 和 `reject` 函数以及 b) 通过 `instanceof Promise` 检查的策略。我的用例是将“Promise”类扩展为“Deferred”类,该类公开其实例的“resolve”和“reject”函数。 (2认同)
  • 我应该补充一点,虽然我认为这个答案描述了扩展 Promise 类的最佳方法,但在 99.9% 的情况下这样做可能不是最明智的做法。不过,如果绝对必须这样做,这种方法效果最好(至少根据我的测试)。 (2认同)