gum*_*ins 7 javascript inheritance es6-promise es6-class
我想使用ES6语法扩展本机Javascript Promise类,并能够在子类构造函数中调用一些异步函数。基于异步功能的结果,承诺必须被拒绝或解决。
但是,then调用函数时会发生两个奇怪的事情:
class MyPromise extends Promise {
constructor(name) {
super((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
this.name = name
}
}
new MyPromise('p1')
.then(result => {
console.log('resolved, result: ', result)
})
.catch(err => {
console.error('err: ', err)
})Run Code Online (Sandbox Code Playgroud)
小智 8
这篇文章asdru包含正确的答案,但也包含一种应该劝阻的方法(构造函数黑客)。
构造函数 hack 检查构造函数参数是否是一个函数。这不是正确的方法,因为 ECMAScript 设计包含通过Symbol.species.
asdru使用的评论Symbol.species是正确的。请参阅当前ECMAScript 规范中的解释:
Promise 原型方法通常使用其 this 值的构造函数来创建派生对象。但是,子类构造函数可以通过重新定义其 @@species 属性来覆盖默认行为。
finally该规范(间接)在和部分引用了此注释then(查找 的提及SpeciesConstructor)。
通过作为物种构造者返回,可以避免答案分析如此清楚的Promise问题。调用构造函数,但不调用子类构造函数。构造函数仅使用参数调用一次,不需要或不适合进一步的参数检查逻辑。traktorthenPromiseMyPromiseMyPromisename
因此,代码应该简单地是:
class MyPromise extends Promise {
constructor(name) {
super((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
this.name = name
}
static get [Symbol.species]() {
return Promise;
}
get [Symbol.toStringTag]() {
return 'MyPromise';
}
}
Run Code Online (Sandbox Code Playgroud)
少即是多!
一些注意事项:
MDN有一个在扩展中使用物种符号的示例Array。
最新的浏览器版本(Chrome、FF、Safari、MAC 和 Linux 上的 Edge)可以正确处理此问题,但我没有有关其他浏览器或旧版本的信息。
Symbol.toStringTag这是一个非常好的接触,但不是必需的。大多数浏览器使用此符号返回的值来标识控制台中的子类 Promise,但是请注意,FF 不会 - 这很容易造成混淆。然而,在所有浏览器中,new MyPromise('mine').toString()都会产生"[object MyPromise]".
如果您使用 Typescript 进行创作,所有这些也都不成问题。
正如所noseratio指出的,扩展 Promise 的主要用例是包装支持中止或取消逻辑(FileReader、fetch 等)的(传统)API。
推理很简单,但不一定很明显。
.then() 回报承诺then在Promise的子类上调用if ,则返回的Promise是该子类的实例,而不是Promise本身。then返回的承诺是通过调用子类的构造,并通过它记录了该值的内部执行程序功能构建resolve,并reject传递给它供以后使用的参数。then在监视onfulfilled或onrejected处理程序的执行情况(稍后)以查看它们是否返回值(解析then返回的承诺)或引发错误(拒绝承诺)时解析或拒绝异步返回的承诺。简而言之,在then内部获取并记录对它们返回的promise resolve和reject函数的引用。
new MyPromise( 'p1')
Run Code Online (Sandbox Code Playgroud)
工作正常,并且是对子类构造函数的第一个调用。
.then( someFunction)
Run Code Online (Sandbox Code Playgroud)
将记录记录someFunction在进行的then呼叫列表中new MyPromise(then可以多次调用),并尝试通过调用来创建返回承诺
new MyPromise( (resolve, reject) => ... /* store resolve reject references */
Run Code Online (Sandbox Code Playgroud)
这是从then代码对子类构造函数的第二次调用。构造函数应该(并且确实)同步返回。
从创建要返回的保证返回时,该.then方法进行完整性检查,以查看其以后使用的resolveand reject函数是否实际上是函数。它们应该已经与调用中提供的回调一起存储在列表中then。
如果MyPromise不是这样的话。甚至没有调用传递then给的执行程序MyPromise。因此,then方法代码将引发类型错误“无法解决Promise resolve或reject函数”-它无法解析或拒绝应返回的诺言。
创建Promise的子类时,子类构造函数必须将执行程序函数作为其第一个参数,并使用实际resolve和reject函数参数调用该执行程序。这是then方法代码内部需要的。
做一些复杂的事情MyPromise,也许检查第一个参数以查看它是否是一个函数,然后将其称为执行程序,可能是可行的,但不在此答案范围内!对于所示的代码,编写工厂/库函数可能更简单:
new MyPromise( 'p1')
Run Code Online (Sandbox Code Playgroud)
Promise的类扩展不是扩展。如果是这样,则需要实现Promise接口并将执行程序函数作为第一个参数。你可以使用一个工厂函数返回一个承诺,其异步解决(如上),或黑客张贴的代码
MyPromise.prototype.constructor = Promise
Run Code Online (Sandbox Code Playgroud)
这会导致.then返回常规的Promise对象。黑客本身驳斥了正在进行类扩展的想法。
我发现延长承诺的最佳方式是
class MyPromise extends Promise {
constructor(name) {
// needed for MyPromise.race/all ecc
if(name instanceof Function){
return super(name)
}
super((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
this.name = name
}
// you can also use Symbol.species in order to
// return a Promise for then/catch/finally
static get [Symbol.species]() {
return Promise;
}
// Promise overrides his Symbol.toStringTag
get [Symbol.toStringTag]() {
return 'MyPromise';
}
}
new MyPromise('p1')
.then(result => {
console.log('resolved, result: ', result)
})
.catch(err => {
console.error('err: ', err)
})
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2020 次 |
| 最近记录: |