web-dev-qa-db-ja.com

解決を開始せずに(ES6)プロミスを作成する

ES6 Promiseを使用して、それを解決するロジックを定義せずにPromiseを作成するにはどうすればよいですか?基本的な例を次に示します(一部のTypeScript):

var promises = {};
function waitFor(key: string): Promise<any> {
  if (key in promises) {
    return promises[key];
  }
  var promise = new Promise(resolve => {
    // But I don't want to try resolving anything here :(
  });

  promises[key] = promise;
  return promise;
}

function resolveWith(key: string, value: any): void {
  promises[key].resolve(value); // Not valid :(
}

他のpromiseライブラリを使用して簡単に実行できます。 JQueryの例:

var deferreds = {};
function waitFor(key: string): Promise<any> {
  if (key in promises) {
    return deferreds[key].promise();
  }
  var def = $.Deferred();    
  deferreds[key] = def;
  return def.promise();
}

function resolveWith(key: string, value: any): void {
  deferreds[key].resolve(value);
}

私がこれを行うことができる唯一の方法は、Promiseのエグゼキューター内のどこかにresolve関数を保存することですが、それは厄介なようで、この関数が正確に実行されるときに定義されているかわかりません-常に構築時にすぐに実行されますか?

ありがとう。

53
Barguast

良い質問!

Promiseコンストラクターに渡されたリゾルバーは、このユースケースをサポートするために意図的に同期的に実行されます。

var deferreds = [];
var p = new Promise(function(resolve, reject){
    deferreds.Push({resolve: resolve, reject: reject});
});

その後、しばらくしてから:

 deferreds[0].resolve("Hello"); // resolve the promise with "Hello"

Promiseコンストラクターが指定される理由は、次のとおりです。

  • 通常(常にではありませんが)、解決ロジックは作成にバインドされています。
  • Promiseコンストラクターはスローセーフであり、例外を拒否に変換します。

適合しない場合があり、そのためにリゾルバは同期的に実行されます。 トピックの関連する読み物です

63

ここに2セントを追加します。 「解決を開始せずにes6 Promiseを作成する」という質問を正確に検討して、代わりにラッパー関数を作成してラッパー関数を呼び出すことを解決しました。コード:

Promiseを返す関数fがあるとしましょう

/** @return Promise<any> */
function f(args) {
   return new Promise(....)
}

// calling f()
f('hello', 42).then((response) => { ... })

ここで、実際に解決せずにf('hello', 42)の呼び出しをprepareしたい:

const task = () => f('hello', 42) // not calling it actually

// later
task().then((response) => { ... })

これが誰かを助けることを願っています:)


コメントで尋ねられたようにPromise.all()を参照し(@Joe Frambachが答えた)、preparef1('super')f2('Rainbow')への呼び出し、2つの関数約束を返す

const f1 = args => new Promise( ... )
const f2 = args => new Promise( ... )

const tasks = [
  () => f1('super'),
  () => f2('Rainbow')
]

// later
Promise.all(tasks.map(t => t()))
  .then(resolvedValues => { ... })
38
Manu

より包括的なアプローチはいかがですか?

.resolve()および.reject()メソッドで装飾された新しいPromiseを返すコンストラクターを作成できます。

おそらく、コンストラクタにDeferredという名前を付けることを選択します。これは、javascript promiseの[歴史]で多くの優先順位を持つ用語です。

_function Deferred(fn) {
    fn = fn || function(){};

    var resolve_, reject_;

    var promise = new Promise(function(resolve, reject) {
        resolve_ = resolve;
        reject_ = reject;
        fn(resolve, reject);
    });

    promise.resolve = function(val) {
        (val === undefined) ? resolve_() : resolve_(val);
        return promise;//for chainability
    }
    promise.reject = function(reason) {
        (reason === undefined) ? reject_() : reject_(reason);
        return promise;//for chainability
    }
    promise.promise = function() {
        return promise.then(); //to derive an undecorated promise (expensive but simple).
    }

    return promise;
}
_

プレーンなオブジェクトではなく装飾されたプロムシーを返すことにより、装飾に加えて、すべてのプロミスの自然なメソッド/プロパティが利用可能になります。

また、fnを処理することにより、Deferredで使用する必要がある/選択した場合に、リビュアーパターンは利用可能なままです。

[〜#〜] demo [〜#〜]

これで、Deferred()ユーティリティが適切に配置されたため、コードはjQueryの例とほぼ同じになりました。

_var deferreds = {};
function waitFor(key: string): Promise<any> {
  if (key in promises) {
    return deferreds[key].promise();
  }
  var def = Deferred();    
  deferreds[key] = def;
  return def.promise();
}
_
3
Roamer-1888