ES6 Promiseを使っています。
通常、約束はこのように構成され使用されます。
new Promise(function(resolve, reject){
if (someCondition){
resolve();
} else {
reject();
}
});
しかし、私は以下のようなことをして、柔軟性のために解決策を外部に取り入れています。
var outsideResolve;
var outsideReject;
new Promise(function(resolve, reject) {
outsideResolve = resolve;
outsideReject = reject;
});
以降
onClick = function(){
outsideResolve();
}
これはうまくいきますが、もっと簡単な方法はありますか?そうでなければ、これは良い習慣ですか?
いいえ、これを実行する他の方法はありません - 私が言えるのは、このユースケースはあまり一般的ではないということです。 Felixがコメントで言ったように - あなたがすることは一貫してうまくいくでしょう。
Promiseコンストラクタがこのように振る舞う理由は安全性を投げることです - あなたのコードがpromiseコンストラクタの中で実行されている間にあなたが予想しなかった例外が起こるなら拒絶に変わるでしょう。拒否は重要であり、予測可能なコードを維持するのに役立ちます。
このthrowの安全性の理由から、promiseコンストラクタは遅延よりも選択されています(これはあなたがしていることを可能にする代替のpromise構築方法です) - ベストプラクティスとして - 要素を渡してpromiseコンストラクタを代わりに使用します。
var p = new Promise(function(resolve, reject){
this.onclick = resolve;
}.bind(this));
このため - あなたがあなたが関数をエクスポートするよりもpromiseコンストラクタを使うことができる時はいつでも - 私はあなたがそれを使うことを勧めます。両方を避けることができるときはいつでも - 両方とチェーンを避けてください。
if(condition)
のようなものにpromiseコンストラクタを使うべきではないことに注意してください。最初の例は次のように書くことができます。
var p = Promise[(someCondition)?"resolve":"reject"]();
シンプル:
var promiseResolve, promiseReject;
var promise = new Promise(function(resolve, reject){
promiseResolve = resolve;
promiseReject = reject;
});
promiseResolve();
ここでパーティーに少し遅れますが、それを行う別の方法は Deferred オブジェクトを使うことです。あなたは本質的に同じ量の定型句を持っています、しかしあなたがそれらを渡してそしておそらくそれらの定義の外で解決したいならそれは便利です。
単純実装:
class Deferred {
constructor() {
this.promise = new Promise((resolve, reject)=> {
this.reject = reject
this.resolve = resolve
})
}
}
function asyncAction() {
var dfd = new Deferred()
setTimeout(()=> {
dfd.resolve(42)
}, 500)
return dfd.promise
}
asyncAction().then(result => {
console.log(result) // 42
})
ES5のバージョン:
function Deferred() {
var self = this;
this.promise = new Promise(function(resolve, reject) {
self.reject = reject
self.resolve = resolve
})
}
function asyncAction() {
var dfd = new Deferred()
setTimeout(function() {
dfd.resolve(42)
}, 500)
return dfd.promise
}
asyncAction().then(function(result) {
console.log(result) // 42
})
私のフレームワークのために2015年に思い付いた解決策。私はこのタイプの約束と言いましたタスク
function createPromise(handler){
var _resolve, _reject;
var promise = new Promise(function(resolve, reject){
_resolve = resolve;
_reject = reject;
handler(resolve, reject);
})
promise.resolve = _resolve;
promise.reject = _reject;
return promise;
}
var promise = createPromise()
promise.then(function(data){ alert(data) })
promise.resolve(200) // resolve from outside
私は@ JohnJaquesの回答が好きでしたが、それをさらに一歩進めたかったのです。
then
とcatch
、そしてDeferred
オブジェクトをバインドすると、それはPromise
APIを完全に実装し、それをpromiseとして扱うことができ、await
などと扱うことができます。
class DeferredPromise {
constructor() {
this._promise = new Promise((resolve, reject) => {
// assign the resolve and reject functions to `this`
// making them usable on the class instance
this.resolve = resolve;
this.reject = reject;
});
// bind `then` and `catch` to implement the same interface as Promise
this.then = this._promise.then.bind(this._promise);
this.catch = this._promise.catch.bind(this._promise);
this[Symbol.toStringTag] = 'Promise';
}
}
const deferred = new DeferredPromise();
console.log('waiting 2 seconds...');
setTimeout(() => {
deferred.resolve('whoa!');
}, 2000);
async function someAsyncFunction() {
const value = await deferred;
console.log(value);
}
someAsyncFunction();
ヘルパーメソッドはこの余分なオーバーヘッドを軽減し、あなたに同じjQueryの感触を与えます。
function Deferred() {
let resolve;
let reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
使い方は
const { promise, resolve, reject } = Deferred();
displayConfirmationDialog({
confirm: resolve,
cancel: reject
});
return promise;
これはjQueryに似ています
const dfd = $.Deferred();
displayConfirmationDialog({
confirm: dfd.resolve,
cancel: dfd.reject
});
return dfd.promise();
ユースケースでは、この単純なネイティブ構文は問題ありませんが
return new Promise((resolve, reject) => {
displayConfirmationDialog({
confirm: resolve,
cancel: reject
});
});
私は "フラットプロミス"と呼ぶものを作成するためにヘルパー関数を使用しています -
function flatPromise() {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
そして私はそれをこんな感じで使っています -
function doSomethingAsync() {
// Get your promise and callbacks
const { resolve, reject, promise } = flatPromise();
// Do something amazing...
setTimeout(() => {
resolve('done!');
}, 500);
// Pass your promise to the world
return promise;
}
完全な動作例を見る -
function flatPromise() {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
function doSomethingAsync() {
// Get your promise and callbacks
const { resolve, reject, promise } = flatPromise();
// Do something amazing...
setTimeout(() => {
resolve('done!');
}, 500);
// Pass your promise to the world
return promise;
}
(async function run() {
const result = await doSomethingAsync()
.catch(err => console.error('rejected with', err));
console.log(result);
})();
編集:私は flat-promise と呼ばれるNPMパッケージを作成しました - そしてコードも入手可能です GitHub上 。
約束をクラスで包むことができます。
class Deferred {
constructor(handler) {
this.promise = new Promise((resolve, reject) => {
this.reject = reject;
this.resolve = resolve;
handler(resolve, reject);
});
this.promise.resolve = this.resolve;
this.promise.reject = this.reject;
return this.promise;
}
promise;
resolve;
reject;
}
// How to use.
const promise = new Deferred((resolve, reject) => {
// Use like normal Promise.
});
promise.resolve(); // Resolve from any context.
はい、できます。ブラウザ環境にCustomEvent
APIを使用する。そして、node.js環境でのEvent Emitterプロジェクトの使用問題のスニペットはブラウザ環境用であるため、ここでは同じ例を示します。
function myPromiseReturningFunction(){
return new Promise(resolve => {
window.addEventListener("myCustomEvent", (event) => {
resolve(event.detail);
})
})
}
myPromiseReturningFunction().then(result => {
alert(result)
})
document.getElementById("p").addEventListener("click", () => {
window.dispatchEvent(new CustomEvent("myCustomEvent", {detail : "It works!"}))
})
<p id="p"> Click me </p>
この答えが役に立つと思います。
解決策は、クロージャを使って解決/拒否機能を保存し、さらに約束を拡張する機能を追加することでした。
これがパターンです:
function getPromise() {
var _resolve, _reject;
var promise = new Promise((resolve, reject) => {
_reject = reject;
_resolve = resolve;
});
promise.resolve_ex = (value) => {
_resolve(value);
};
promise.reject_ex = (value) => {
_reject(value);
};
return promise;
}
そしてそれを使う:
var promise = getPromise();
promise.then(value => {
console.info('The promise has been fulfilled: ' + value);
});
promise.resolve_ex('hello');
// or the reject version
//promise.reject_ex('goodbye');
ここでの答えの多くは、 この記事 の最後の例に似ています。私は複数のPromiseをキャッシュしています、そしてresolve()
とreject()
関数はどんな変数またはプロパティにも割り当てることができます。その結果、このコードをもう少しコンパクトにすることができます。
function defer(obj) {
obj.promise = new Promise((resolve, reject) => {
obj.resolve = resolve;
obj.reject = reject;
});
}
これは、このバージョンのdefer()
を使用してFontFace
load Promiseを別の非同期プロセスと組み合わせる簡単な例です。
function onDOMContentLoaded(evt) {
let all = []; // array of Promises
glob = {}; // global object used elsewhere
defer(glob);
all.Push(glob.promise);
// launch async process with callback = resolveGlob()
const myFont = new FontFace("myFont", "url(myFont.woff2)");
document.fonts.add(myFont);
myFont.load();
all.Push[myFont];
Promise.all(all).then(() => { runIt(); }, (v) => { alert(v); });
}
//...
function resolveGlob() {
glob.resolve();
}
function runIt() {} // runs after all promises resolved
拒否をハイジャックして返すための関数を作成するのはどうですか。
function createRejectablePromise(handler) {
let _reject;
const promise = new Promise((resolve, reject) => {
_reject = reject;
handler(resolve, reject);
})
promise.reject = _reject;
return promise;
}
// Usage
const { reject } = createRejectablePromise((resolve) => {
setTimeout(() => {
console.log('resolved')
resolve();
}, 2000)
});
reject();
最初にブラウザまたはノードで--allow-natives-syntaxを有効にします
const p = new Promise(function(resolve, reject){
if (someCondition){
resolve();
} else {
reject();
}
});
onClick = function () {
%ResolvePromise(p, value)
}
私はその仕事をする要旨をまとめました。 https://Gist.github.com/thiagoh/c24310b562d50a14f3e7602a82b4ef1
使い方は次のとおりです。
import ExternalizedPromiseCreator from '../externalized-promise';
describe('ExternalizedPromise', () => {
let fn: jest.Mock;
let deferredFn: jest.Mock;
let neverCalledFn: jest.Mock;
beforeEach(() => {
fn = jest.fn();
deferredFn = jest.fn();
neverCalledFn = jest.fn();
});
it('resolve should resolve the promise', done => {
const externalizedPromise = ExternalizedPromiseCreator.create(() => fn());
externalizedPromise
.promise
.then(() => deferredFn())
.catch(() => neverCalledFn())
.then(() => {
expect(deferredFn).toHaveBeenCalled();
expect(neverCalledFn).not.toHaveBeenCalled();
done();
});
expect(fn).toHaveBeenCalled();
expect(neverCalledFn).not.toHaveBeenCalled();
expect(deferredFn).not.toHaveBeenCalled();
externalizedPromise.resolve();
});
...
});
Promise
の代わりにドロップインとして機能するmanual-promise
というライブラリを作成しました。ここでの他の答えは、プロキシまたはラッパーを使用するため、Promise
の代わりにドロップインとして機能しません。
yarn add manual-promise
npn install manual-promise
import { ManualPromise } from "manual-promise";
const prom = new ManualPromise();
prom.resolve(2);
// actions can still be run inside the promise
const prom2 = new ManualPromise((resolve, reject) => {
// ... code
});
new ManualPromise() instanceof Promise === true
私はこれのために小さなライブラリを書きました。 https://www.npmjs.com/package/@inf3rno/promise.exposed
私は他の人が書いたファクトリメソッドアプローチを使用しましたが、私はthen
、catch
、finally
メソッドもオーバーライドしたので、それらによっても元の約束を解決することができます。
外部からexecutorを使わずにPromiseを解決する:
const promise = Promise.exposed().then(console.log);
promise.resolve("This should show up in the console.");
外部からexecutorのsetTimeoutを使用して競合します。
const promise = Promise.exposed(function (resolve, reject){
setTimeout(function (){
resolve("I almost fell asleep.")
}, 100000);
}).then(console.log);
setTimeout(function (){
promise.resolve("I don't want to wait that much.");
}, 100);
グローバルな名前空間を汚染したくないのであれば、競合しないモードがあります。
const createExposedPromise = require("@inf3rno/promise.exposed/noConflict");
const promise = createExposedPromise().then(console.log);
promise.resolve("This should show up in the console.");
受け入れられた答えは間違っています。スコープと参照を使用すると、かなり簡単になりますが、Promise puristsが怒ります。
const createPromise = () => {
let resolver;
return [
new Promise((resolve, reject) => {
resolver = resolve;
}),
resolver,
];
};
const [ promise, resolver ] = createPromise();
promise.then(value => console.log(value));
setTimeout(() => resolver('foo'), 1000);
基本的に、promiseが作成されたときにresolve関数への参照を取得しており、外部で設定できるようにそれを返します。
コンソールは1秒で出力します:
> foo