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ポリフィルを使用しています
注:finally
はJavaScriptのpromiseの標準的な部分なので、次のようにします。
thePromise.then(result => doSomething(result)
.catch(error => handleOrReportError(error))
.finally(() => doSomethingAfterFulfillmentOrRejection());
finally
が標準であった以前の回答:
catch
から値を返す場合、then
の結果に対してcatch
を使用できます。
thePromise.then(result => doSomething(result)
.catch(error => handleErrorAndReturnSomething(error))
.then(resultOrReturnFromCatch => /* ... */);
...しかし、それは拒否をフルフィルメントに変換することを意味し(拒否されたプロミスをスローまたは返すのではなく、catch
から何かを返すことによって)、その事実に依存します。
フルフィルメント/リジェクションを変更せずに透過的に通過させるものが必要な場合、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
などの機能と一致しており、サブクラス化されたpromiseのサポートの重要な部分です。
上記は、意図的にfinally
コールバックへの引数を含まないであり、約束が順守されたか拒否されたかを示すものではありません。従来のtry-catch-finally
構造のfinally
と一致するようにします。しかし、必要に応じて、その情報の一部をコールバックに簡単に渡すことができます。
同様に、上記はfinally
コールバックによって返された値を使用しませんexcept約束である場合、約束がチェーンを続行する前に解決します。
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ポリフィルに統合する最も簡単な方法だと思います。
または、プロトタイプを変更するのではなく、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では、ユーティリティ関数はextremely使用するのが面倒です。これは、動作するという約束を渡す必要があるため、完全に機能しなくなるためです。通常の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));