有没有办法使用javascript本机承诺(docs)来创建一个承诺并附加那些,而不知道构造函数时间它将如何解决?
var foo = new Promise(function(resolve, reject) {
// I don't know how this will resolve yet as some other object will resolve it
});
foo.then(function(val) {
console.log("first " + val);
});
foo.resolve("bar");
foo.then(function(val) {
console.log("second " + val);
});
// result
// first bar
// second bar
答案 0 :(得分:5)
如果承诺的结果取决于其他承诺,您应该使用then
创建承诺。
@Norguard以直接形式提出的内容没有多大意义(它甚至被创造为deferred anti-pattern)。下面的代码完全相同,不需要额外的承诺:
var deferredSomething = function () {
return waitAWhile()
.then(doStuff)
.then(function (result) {
if (result.isGood) {
return result.data;
} else {
throw result.error;
}
});
});
};
而且,即使由于某种原因你需要提前创建承诺,那么使用构造函数模式,这样做会更清晰:
var deferredSomething = function () {
return new Promise(function (resolve, reject) {
waitAWhile()
.then(doStuff)
.then(function (result) {
if (result.isGood) {
resolve(result.data);
} else {
reject(result.error);
}
});
});
};
答案 1 :(得分:4)
只需将它们保存在封闭内。
var makePromise = function () {
var resolvePromise = null,
rejectPromise = null,
promise = new Promise(function (resolve, reject) {
resolvePromise = resolve;
rejectPromise = reject;
});
return { promise : promise, resolve : resolvePromise, reject : rejectPromise };
};
var deferredSomething = function () {
var deferredThing = makePromise();
waitAWhile()
.then(doStuff)
.then(function (result) {
if (result.isGood) {
deferredThing.resolve(result.data);
} else {
deferredThing.reject(result.error);
}
});
return deferredThing.promise;
};
这实际上是“延迟”概念和“承诺”概念之间的大部分差异;在您将.then|.success|.done|etc...
传递给消费者的同时,还有一个级别,它具有您可以提供给其他人的实际遥控器。
一旦你将这些功能带入你的上游流程,你可以愉快地懒得加载任何你喜欢的东西,使用你将要返回的“thenable”,然后成功或失败你的链(或让它挂起)随意...
看到这可能会继续成为选择的答案,并继续被拒绝,作为他所遇到的确切问题的解决方案(即:改造代码,而不是用ES6承诺的话),我想我会添加一个更详细的例子,说明为什么有选择地使用这个反模式可能比没有更好:
MongoClient.connect("mongodb://localhost:21017/mydb", (err, db) => {
db.collection("mycollection", (err, collection) => {
collection.find().toArray((err, results) => {
doStuff(results);
});
});
});
如果我要写一个库,在这里,希望能达到我能写的地步:
let dbConnected = MongoClient.connect(dbURL);
dbConnected
.then(db => db.collection(myCollection))
.then(collection => collection.find(query))
.then(stream => doStuff(stream));
......或者:
composeAsync(
(stream) => doStuff(stream),
(collection) => collection.find(query),
(db) => dbCollection(myCollection)
)(dbConnected);
...为了便于在库中使用,将每个函数体包装在实例化的promise中是否有意义 // find = curry(查询,集合) 返回新的Promise(解决,拒绝){ / *整个函数体,这里 / / 做了许多与mongo.db.collection.find的解析无关的东西,但是与它的调用相关* / collection.find(query).toArray(/ node-callback /(err,result){ if(错误){ 拒绝(ERR); } else { 解决(结果); } }); };
...或者在查看真正只需要解析特定于节点的回调的模式时,使用某种形式的promise-resolver更有意义,以节省必须写出/复制粘贴a应该完全干掉的六条纯冗余线?
// find = curry(query, collection)
let resolver = new NodeResolver();
collection.find(query).toArray(promise.resolve);
return resolver.promise;
是的,这是一种反模式......然而,反模式需要更少的击键,恢复了承诺链的自然流程,修复了Node的回调专用API的问题,降低了错误,等等。
是的,已经有图书馆这样做了...... ...特定于X库或Y的解决方案...... ...或全局覆盖各种模块方法的解决方案(可怕) ...或者解决方案,它们基本上迫使你传递你正在进行的电话的所有细节:
wrapNodeMethod(fs, "read", url, config).then(data => { /*...*/ });
但是没有一个简单的解决方案可以解决所有这些痛苦,但没有:
a)将整个函数体包装在一个promise中,以便为一个解析器提供异步回调 b)在库中使用反模式,以便向Node回调传递一个解析器,其中函数体的其余部分需要准确地知道。
即使需要在解析器中转换数据,在新的承诺中返回转换后的集合仍然更有意义
let resolver = new NodeResolver();
somethingAsync(resolver.resolve);
return resolver.promise.then(transformData).then(logTransform);
...而不是为了封闭范围参考而包裹整个身体,包括变换等,只是为了避免“反模式”,这显然违背了已成为一个非常突出的JS平台的粒度/范例
现在,就个人而言,如果IO || Node方法返回一个promise和/或一个流,并将回调作为平台的核心部分,我会更高兴... ......那不会发生......
...但你不可能告诉我,尽管仍在使用ES6 Promises,但是编写更少,保持Node模块DRY是一个“反模式”,因此没有为我提供更有说服力的解决方案。
如果你能为我提供一些我可以在任何 NodeJS回调中使用的东西,这确实提供了一个更有说服力的解决方案,这样我就不需要换行在新构造函数中包含异步回调的每个方法的每个主体,或者使用笨重的调度程序方法,或者劫持整个模块来覆盖它们的全局功能......
...我非常愿意撤回我关于这种特殊模式仍然非常有用的声明,关于与易受金字塔问题接触的API的接口。