web-dev-qa-db-ja.com

Promises(サーバー側ES6)を使用して非線形の非依存制御フローを実現するにはどうすればよいですか?

Javaの世界からやって来て、マルチスレッドアプローチをIOにaysncIOのES6Promisesコンセプトに変換するのに問題があります。多くの例私は約束で線形の流れを示すのを見てきました。

promiseFunction
    .then(functionThatReturnsAnotherPromise)
    .then(functionThatReturnsYetAnotherPromise)
    ...

非線形フローを示す例は、先に進む前にallの約束を完了しなければならない関数を示しています。

Promise.all([functionThatReturnsOnePromise, functionThatReturnsAnotherPromise])
    .then(functionThatUsesBothReturnValues);

私がやろうとしているのは、各ブランチが別のブランチに依存しないツリーベースの制御フローです。私の制御フローを示すこのチャートを考えてみましょう。

Program's tree-style control flow

  • (1)プロジェクトを取得するために非同期RESTリクエストを作成します
  • (2)5つのプロジェクトを受け取りました。受信したプロジェクトごとに、(a)プロジェクトインスタンスを作成し、(b)非同期RESTリクエストを作成して、そのプロジェクトのチームを取得します
  • (3-7)xチームを受け取りました。受信したチームごとに、(a)チームインスタンスを作成し、(b)チームをその親プロジェクトインスタンスにメンバーとして追加し、(c)REST呼び出しを行って、すべてのチームを取得します-そのチームのメンバー
  • (8-21)xチームメンバーを受け取りました。チームメンバーごとに、(a)チームメンバーインスタンスを作成し、(b)それをメンバーとして親チームインスタンスに追加します。

ここで注意すべき重要なことは、8-21が発生するために、3-7がallを実行する必要がないということです。基本的に、ここで達成しようとしているのは、2の応答を受信したら、3、4、5、6、および7を実行することです。3が完了したら(4〜7は気にしないで)、8〜10を実行します。

ただし、8-21は3-7の完了に依存していないすべてではないため、このツリーのような制御フローを実現するためにどのPromiseが構築するかはわかりません。

getAllProjects()
    .then(function(responseJson) {
        var promises = [];
        for (var index in responseJson.value) {
            var project = new Project(responseJson.value[index]);
            promises.Push(getTeamsForProject(project));
        }
        return promises;
    })
    .thenAsEachPromiseCompletes(function(responseJson) {
        var promises = [];
        for (var index in responseJson.value) {
            var team = new Team(responseJson.value[index]);
            promises.Push(getTeamMembersForTeam(team));
        }
        return promises;
    })
    .thenAsEachPromiseCompletes(function(responseJson) {
        ...
    });
2
Michael Plautz

主な問題は、どこで分岐し、どこで非同期実行に参加するかということのようです。サンプルコードでは、単一のポイントを使用して、依存しない操作を結合しようとしています。ここでの秘訣は、多くのブランチと結合を使用することです。一連のプロジェクトがあり、チームをロードするとします。次に、Promiseのリストがあり、それぞれについて新しいブランチを開始し、新しいPromiseを作成します。正しくチェーンされると、物事は明確になり始め、よりシンプルに見えます。

getAllProjects().then(function(projects) {
    return Promise.all(projects.map(function (project) {
        return getTeamsForProject(project).then(function (teams) {
            return Promise.all(teams.map(function (team) {
                project.addTeam(team);
                return getTeamMembers(team).then(function (members) {
                    // ...
                });
            }));
        }).then(function () {
            return project;
        });
    }));
}).then(function (projects) {
    // all done
});

これは少し厄介ですが、いつでも次のようなよりクリーンなものにリファクタリングできます。

getAllProjects().then(function(projects) {
    return Promise.all(projects.map(fetchProjectTeams));
}).then(function (projects) {
    // all done
});

function fetchProjectTeams(project) {
    return getTeamsForProject(project).then(function (teams) {
        return addProjectTeams(teams, project);
    }).then(function () {
        return project;
    });
}

function addProjectTeams(teams, project) {
    return Promise.all(teams.map(function (team) {
        project.addTeam(team);
        return fetchTeamMembers(team);
    }));
}

function fetchTeamMembers(team) {
    return getTeamMembers(team).then(function (members) {
        // ...
    });
}

最後の例では、この複雑な操作が複数のわかりやすいステップに分割されています。

3
Bruno Schäpper