承诺:不管resolve/reject如何执行某事?
Promises: Execute something regardless of resolve/reject?
使用 Promises 设计模式,是否可以实现以下内容:
var a, promise
if promise.resolve
a = promise.responsevalue;
if promise.reject
a = "failed"
AFTER resolution/rejection. Not ASYNC!!
send a somewhere, but not asynchronously. //Not a promise
我要找的是 try - catch
情况下的 finally
。
PS:我在 NodeJS 上使用 ES6 Promise polyfill
注意: finally
现在是 JavaScript 承诺的标准部分,因此您可以这样做:
thePromise.then(result => doSomething(result)
.catch(error => handleOrReportError(error))
.finally(() => doSomethingAfterFulfillmentOrRejection());
finally
之前的回答是标准的:
如果您 return 来自 catch
的值,那么您可以只对 catch
.
的结果使用 then
thePromise.then(result => doSomething(result)
.catch(error => handleErrorAndReturnSomething(error))
.then(resultOrReturnFromCatch => /* ... */);
...但这意味着您正在将拒绝转化为实现(通过 returning 来自 catch
的内容而不是抛出或 returning 被拒绝的承诺),并依赖于这一事实。
如果你想要透明地传递 fulfillment/rejection 而不修改它的东西,那么 ES2015 ("ES6") 中没有任何东西可以做到这一点 (edit:又是现在),但是好写(这个是ES2015的,不过我下面有一个ES5的翻译):
{
let worker = (p, f, done) => {
return p.constructor.resolve(f()).then(done, done);
};
Object.defineProperty(Promise.prototype, "finally", {
value(f) {
return this.then(
result => worker(this, f, () => result),
error => worker(this, f, () => { throw error; })
);
}
});
}
示例:
{
let worker = (p, f, done) => {
return p.constructor.resolve(f()).then(done, done);
};
Object.defineProperty(Promise.prototype, "finally", {
value(f) {
return this.then(
result => worker(this, f, () => result),
error => worker(this, f, () => { throw error; })
);
}
});
}
test("p1", Promise.resolve("good")).finally(
() => {
test("p2", Promise.reject("bad"));
}
);
function test(name, p) {
return p.then(
result => {
console.log(name, "initial fulfillment:", result);
return result;
},
error => {
console.log(name, "initial rejection; propagating it");
throw error;
}
)
.finally(() => {
console.log(name, "in finally");
})
.then(
result => {
console.log(name, "fulfilled:", result);
},
error => {
console.log(name, "rejected:", error);
}
);
}
一些注意事项:
注意 this.constructor
的使用,这样我们就可以调用 resolve
任何类型的承诺(包括可能的子类)创建原始承诺;这与 Promise.resolve
和其他人的工作方式一致,并且是支持子类承诺的重要部分。
以上内容有意 不 包括 finally
回调的任何参数,并且没有指示承诺是被履行还是被拒绝,在为了与经典的try-catch-finally
结构中的finally
保持一致。但如果需要,可以轻松地将其中一些信息传递到回调中。
类似地,上面不使用 finally
回调 ed return 的值 除了 如果它是一个承诺,它在允许链继续之前等待承诺解决。
这是它的 ES5 翻译:
(function() {
function worker(ctor, f, done) {
return ctor.resolve(f()).then(done, done);
}
Object.defineProperty(Promise.prototype, "finally", {
value: function(f) {
var ctor = this.constructor;
return this.then(
function(result) {
return worker(ctor, f, function() {
return result;
});
},
function(error) {
return worker(ctor, f, function() {
throw error;
});
}
);
}
});
})();
示例:
(function() {
function worker(ctor, f, done) {
return ctor.resolve(f()).then(done, done);
}
Object.defineProperty(Promise.prototype, "finally", {
value: function(f) {
var ctor = this.constructor;
return this.then(
function(result) {
return worker(ctor, f, function() {
return result;
});
},
function(error) {
return worker(ctor, f, function() {
throw error;
});
}
);
}
});
})();
test("p1", Promise.resolve("good")).finally(function() {
test("p2", Promise.reject("bad"));
});
function test(name, p) {
return p.then(
function(result) {
console.log(name, "initial fulfillment:", result);
return result;
},
function(error) {
console.log(name, "initial rejection; propagating it");
throw error;
}
)
.finally(function() {
console.log(name, "in finally");
})
.then(
function(result) {
console.log(name, "fulfilled:", result);
},
function(error) {
console.log(name, "rejected:", error);
}
);
}
我认为这是将此功能集成到 ES5 中的 Promise polyfill 中的最简单方法。
或者如果你更喜欢继承 Promise
而不是修改它的原型:
let PromiseX = (() => {
let worker = (p, f, done) => {
return p.constructor.resolve(f()).then(done, done);
};
class PromiseX extends Promise {
finally(f) {
return this.then(
result => worker(this, f, () => result),
error => worker(this, f, () => { throw error; })
);
}
}
PromiseX.resolve = Promise.resolve;
PromiseX.reject = Promise.reject;
return PromiseX;
})();
示例:
let PromiseX = (() => {
let worker = (p, f, done) => {
return p.constructor.resolve(f()).then(done, done);
};
class PromiseX extends Promise {
finally(f) {
return this.then(
result => worker(this, f, () => result),
error => worker(this, f, () => { throw error; })
);
}
}
PromiseX.resolve = Promise.resolve;
PromiseX.reject = Promise.reject;
return PromiseX;
})();
test("p1", PromiseX.resolve("good")).finally(
() => {
test("p2", PromiseX.reject("bad"));
}
);
function test(name, p) {
return p.then(
result => {
console.log(name, "initial fulfillment:", result);
return result;
},
error => {
console.log(name, "initial rejection; propagating it");
throw error;
}
)
.finally(() => {
console.log(name, "in finally");
})
.then(
result => {
console.log(name, "fulfilled:", result);
},
error => {
console.log(name, "rejected:", error);
}
);
}
您曾说过要在不扩展 Promise.prototype
或 子类的情况下执行此操作。在 ES5 中,效用函数使用起来 非常 笨拙,因为您必须向它传递要执行的 promise,这与正常的 promise 用法完全不一致。在 ES2015 中,可以做一些更自然的事情,但调用起来比修改原型或子类化更痛苦:
let always = (() => {
let worker = (f, done) => {
return Promise.resolve(f()).then(done, done);
};
return function always(f) {
return [
result => worker(f, () => result),
error => worker(f, () => { throw error; })
];
}
})();
用法:
thePromise.then(...always(/*..your function..*/)).
注意扩展运算符的使用(这就是为什么这在 ES5 中不起作用),因此 always
可以为 then
.
提供两个参数
示例:
let always = (() => {
let worker = (f, done) => {
return Promise.resolve(f()).then(done, done);
};
return function always(f) {
return [
result => worker(f, () => result),
error => worker(f, () => { throw error; })
];
}
})();
test("p1", Promise.resolve("good")).then(...always(
() => {
test("p2", Promise.reject("bad"));
}
));
function test(name, p) {
return p.then(
result => {
console.log(name, "initial fulfillment:", result);
return result;
},
error => {
console.log(name, "initial rejection; propagating it");
throw error;
}
)
.then(...always(() => {
console.log(name, "in finally");
}))
.then(
result => {
console.log(name, "fulfilled:", result);
},
error => {
console.log(name, "rejected:", error);
}
);
}
在评论中,您表达了对 finally
不等待承诺的担忧;这又是最后一个 always
示例,延迟证明它确实如此:
let always = (() => {
let worker = (f, done) => {
return Promise.resolve(f()).then(done, done);
};
return function always(f) {
return [
result => worker(f, () => result),
error => worker(f, () => { throw error; })
];
}
})();
test("p1", 500, false, "good").then(...always(
() => {
test("p2", 500, true, "bad");
}
));
function test(name, delay, fail, value) {
// Make our test promise
let p = new Promise((resolve, reject) => {
console.log(name, `created with ${delay}ms delay before settling`);
setTimeout(() => {
if (fail) {
console.log(name, "rejecting");
reject(value);
} else {
console.log(name, "fulfilling");
resolve(value);
}
}, delay);
});
// Use it
return p.then(
result => {
console.log(name, "initial fulfillment:", result);
return result;
},
error => {
console.log(name, "initial rejection; propagating it");
throw error;
}
)
.then(...always(() => {
console.log(name, "in finally");
}))
.then(
result => {
console.log(name, "fulfilled:", result);
},
error => {
console.log(name, "rejected:", error);
}
);
}
ES2015 代码:
promise.then(val => val).catch(() => "failed").then(a => doSomethigWithA(a));
使用 Promises 设计模式,是否可以实现以下内容:
var a, promise
if promise.resolve
a = promise.responsevalue;
if promise.reject
a = "failed"
AFTER resolution/rejection. Not ASYNC!!
send a somewhere, but not asynchronously. //Not a promise
我要找的是 try - catch
情况下的 finally
。
PS:我在 NodeJS 上使用 ES6 Promise polyfill
注意: finally
现在是 JavaScript 承诺的标准部分,因此您可以这样做:
thePromise.then(result => doSomething(result)
.catch(error => handleOrReportError(error))
.finally(() => doSomethingAfterFulfillmentOrRejection());
finally
之前的回答是标准的:
如果您 return 来自 catch
的值,那么您可以只对 catch
.
then
thePromise.then(result => doSomething(result)
.catch(error => handleErrorAndReturnSomething(error))
.then(resultOrReturnFromCatch => /* ... */);
...但这意味着您正在将拒绝转化为实现(通过 returning 来自 catch
的内容而不是抛出或 returning 被拒绝的承诺),并依赖于这一事实。
如果你想要透明地传递 fulfillment/rejection 而不修改它的东西,那么 ES2015 ("ES6") 中没有任何东西可以做到这一点 (edit:又是现在),但是好写(这个是ES2015的,不过我下面有一个ES5的翻译):
{
let worker = (p, f, done) => {
return p.constructor.resolve(f()).then(done, done);
};
Object.defineProperty(Promise.prototype, "finally", {
value(f) {
return this.then(
result => worker(this, f, () => result),
error => worker(this, f, () => { throw error; })
);
}
});
}
示例:
{
let worker = (p, f, done) => {
return p.constructor.resolve(f()).then(done, done);
};
Object.defineProperty(Promise.prototype, "finally", {
value(f) {
return this.then(
result => worker(this, f, () => result),
error => worker(this, f, () => { throw error; })
);
}
});
}
test("p1", Promise.resolve("good")).finally(
() => {
test("p2", Promise.reject("bad"));
}
);
function test(name, p) {
return p.then(
result => {
console.log(name, "initial fulfillment:", result);
return result;
},
error => {
console.log(name, "initial rejection; propagating it");
throw error;
}
)
.finally(() => {
console.log(name, "in finally");
})
.then(
result => {
console.log(name, "fulfilled:", result);
},
error => {
console.log(name, "rejected:", error);
}
);
}
一些注意事项:
注意
this.constructor
的使用,这样我们就可以调用resolve
任何类型的承诺(包括可能的子类)创建原始承诺;这与Promise.resolve
和其他人的工作方式一致,并且是支持子类承诺的重要部分。以上内容有意 不 包括
finally
回调的任何参数,并且没有指示承诺是被履行还是被拒绝,在为了与经典的try-catch-finally
结构中的finally
保持一致。但如果需要,可以轻松地将其中一些信息传递到回调中。类似地,上面不使用
finally
回调 ed return 的值 除了 如果它是一个承诺,它在允许链继续之前等待承诺解决。
这是它的 ES5 翻译:
(function() {
function worker(ctor, f, done) {
return ctor.resolve(f()).then(done, done);
}
Object.defineProperty(Promise.prototype, "finally", {
value: function(f) {
var ctor = this.constructor;
return this.then(
function(result) {
return worker(ctor, f, function() {
return result;
});
},
function(error) {
return worker(ctor, f, function() {
throw error;
});
}
);
}
});
})();
示例:
(function() {
function worker(ctor, f, done) {
return ctor.resolve(f()).then(done, done);
}
Object.defineProperty(Promise.prototype, "finally", {
value: function(f) {
var ctor = this.constructor;
return this.then(
function(result) {
return worker(ctor, f, function() {
return result;
});
},
function(error) {
return worker(ctor, f, function() {
throw error;
});
}
);
}
});
})();
test("p1", Promise.resolve("good")).finally(function() {
test("p2", Promise.reject("bad"));
});
function test(name, p) {
return p.then(
function(result) {
console.log(name, "initial fulfillment:", result);
return result;
},
function(error) {
console.log(name, "initial rejection; propagating it");
throw error;
}
)
.finally(function() {
console.log(name, "in finally");
})
.then(
function(result) {
console.log(name, "fulfilled:", result);
},
function(error) {
console.log(name, "rejected:", error);
}
);
}
我认为这是将此功能集成到 ES5 中的 Promise polyfill 中的最简单方法。
或者如果你更喜欢继承 Promise
而不是修改它的原型:
let PromiseX = (() => {
let worker = (p, f, done) => {
return p.constructor.resolve(f()).then(done, done);
};
class PromiseX extends Promise {
finally(f) {
return this.then(
result => worker(this, f, () => result),
error => worker(this, f, () => { throw error; })
);
}
}
PromiseX.resolve = Promise.resolve;
PromiseX.reject = Promise.reject;
return PromiseX;
})();
示例:
let PromiseX = (() => {
let worker = (p, f, done) => {
return p.constructor.resolve(f()).then(done, done);
};
class PromiseX extends Promise {
finally(f) {
return this.then(
result => worker(this, f, () => result),
error => worker(this, f, () => { throw error; })
);
}
}
PromiseX.resolve = Promise.resolve;
PromiseX.reject = Promise.reject;
return PromiseX;
})();
test("p1", PromiseX.resolve("good")).finally(
() => {
test("p2", PromiseX.reject("bad"));
}
);
function test(name, p) {
return p.then(
result => {
console.log(name, "initial fulfillment:", result);
return result;
},
error => {
console.log(name, "initial rejection; propagating it");
throw error;
}
)
.finally(() => {
console.log(name, "in finally");
})
.then(
result => {
console.log(name, "fulfilled:", result);
},
error => {
console.log(name, "rejected:", error);
}
);
}
您曾说过要在不扩展 Promise.prototype
或 子类的情况下执行此操作。在 ES5 中,效用函数使用起来 非常 笨拙,因为您必须向它传递要执行的 promise,这与正常的 promise 用法完全不一致。在 ES2015 中,可以做一些更自然的事情,但调用起来比修改原型或子类化更痛苦:
let always = (() => {
let worker = (f, done) => {
return Promise.resolve(f()).then(done, done);
};
return function always(f) {
return [
result => worker(f, () => result),
error => worker(f, () => { throw error; })
];
}
})();
用法:
thePromise.then(...always(/*..your function..*/)).
注意扩展运算符的使用(这就是为什么这在 ES5 中不起作用),因此 always
可以为 then
.
示例:
let always = (() => {
let worker = (f, done) => {
return Promise.resolve(f()).then(done, done);
};
return function always(f) {
return [
result => worker(f, () => result),
error => worker(f, () => { throw error; })
];
}
})();
test("p1", Promise.resolve("good")).then(...always(
() => {
test("p2", Promise.reject("bad"));
}
));
function test(name, p) {
return p.then(
result => {
console.log(name, "initial fulfillment:", result);
return result;
},
error => {
console.log(name, "initial rejection; propagating it");
throw error;
}
)
.then(...always(() => {
console.log(name, "in finally");
}))
.then(
result => {
console.log(name, "fulfilled:", result);
},
error => {
console.log(name, "rejected:", error);
}
);
}
在评论中,您表达了对 finally
不等待承诺的担忧;这又是最后一个 always
示例,延迟证明它确实如此:
let always = (() => {
let worker = (f, done) => {
return Promise.resolve(f()).then(done, done);
};
return function always(f) {
return [
result => worker(f, () => result),
error => worker(f, () => { throw error; })
];
}
})();
test("p1", 500, false, "good").then(...always(
() => {
test("p2", 500, true, "bad");
}
));
function test(name, delay, fail, value) {
// Make our test promise
let p = new Promise((resolve, reject) => {
console.log(name, `created with ${delay}ms delay before settling`);
setTimeout(() => {
if (fail) {
console.log(name, "rejecting");
reject(value);
} else {
console.log(name, "fulfilling");
resolve(value);
}
}, delay);
});
// Use it
return p.then(
result => {
console.log(name, "initial fulfillment:", result);
return result;
},
error => {
console.log(name, "initial rejection; propagating it");
throw error;
}
)
.then(...always(() => {
console.log(name, "in finally");
}))
.then(
result => {
console.log(name, "fulfilled:", result);
},
error => {
console.log(name, "rejected:", error);
}
);
}
ES2015 代码:
promise.then(val => val).catch(() => "failed").then(a => doSomethigWithA(a));