GoogleTagManagerの優先度(priority)とAdobeDataCollectionの優先度(Order)の挙動の違い

GoogleTagManager(以下GTM)には、「タグ配信の優先度」という機能がありますよね。
タグの詳細設定の箇所にある設定で、その名の通りタグ配信における優先度を決めます。
(値が大きいほど、先に処理を開始します。)

細かいことについては 【GTM】タグ配信の優先度 / タグの順序付け と 非同期処理を待つ実装 で説明していますのでご覧ください。
一方で、AdobeDataCollection(旧: Adobe Launch | 以下 ADC) にも優先度の項目があります。
この記事では、両者の挙動がどう異なるのか、あるいはほぼ同じなのか、のようなことを深堀りしていきます。
目次[非表示]
- 1.用語整理
- 2.ADCの優先度設定
- 3.GTM:優先度の挙動
- 4.ADC: 優先度の挙動
- 4.1.第1フェーズ
- 4.2.第2フェーズ
- 4.3.各registry は orderを担保している
- 4.4.Ruleの発火
- 5.GTMとADCの異なる点
- 6.まとめ
- 6.1.パフォーマンス面の違い
用語整理
GTMについてはよく知っている方も多いと思いますが
今回ADCについても触れますので簡単に対応表を書くと以下のような形です。
レイヤー | GTM | Adobe Data Collection | 対応関係 | 補足 |
|---|---|---|---|---|
実行単位(入れ物) | タグ(Tag) | ルール(Rule) | 完全一致ではない | Ruleは Event / Condition / Action をまとめる箱 |
実行処理(本体) | タグ(Tag) | アクション(Action) | ここが実質対応 | GTMのタグに最も近いのは Adobe の Action |
発火タイミング | トリガー(Trigger) | イベント(Event) | 一部対応 | PageView, DOMReadyなど / Adobeでは条件判定が Condition に分離される |
発火条件 | トリガー内条件 / 例外トリガー | 条件(Condition) | 分離対応 | pageUrlなど / GTMではTriggerに含まれがちな条件を、Adobeでは独立要素として扱う |
データ取得・加工 | 変数(Variable) | データ要素(Data Element) | ほぼ1:1 | 値の取得・加工・参照に使う |
実際の画面を見たほうがわかりやすいですかね。
Ruleの設定画面は以下のような感じです。

Ruleという箱の中下記が設定されています。
Events:発火タイミング / GTMの ページビュー, DOMReady, ウィンドウの読み込み
Conditions: 条件 / GTMのPageURLやPagePathなどの条件
Actions: 実行されるもの / GTMの カスタムHTMLや、Google広告テンプレートなどのタグ
ADCの優先度設定
さて、ADCにも優先度の設定があるといいましたが、どこにあるのでしょうか。
GTMはタグ単位での設定でしたね。
実は ADCは Rule単位や Action単位ではなく、
「Rule × Events の組み合わせ(ペア)単位」で決まります。
ん?どゆこと?
まず、優先度を設定できる項目は「Events」になります。
「Rule」単位や「Action」単位ではありません。
例えば、先程の添付画像に「Page Top」の Eventsがありますが
その中で Order を指定できます。
なお、ADCは「order」の値が小さい方が優先度が高いです。(GTMとは逆です。)

また、先程の画像に「DOM Ready」もありましたね。
当然、そちらも同様に order を設定できます。

もう1度先程の画像を貼りますね。

GTMと決定的に異なるところは
Eventsごとに優先度を変えられるという点です。
上の画像の Rule は Events(GTMのトリガー)として
PageTop(GTMのページビュー)と DOM Ready が or 条件として設定してあります。
つまり、ページビューのタイミングとDOM Readyのタイミングで発火します。
そして、Eventsそれぞれに Order(優先度)を設定できます。
例えば、
ページビューとして発火するときは Order 50
DOM Ready として発火するときは Order 20
のように 同じルールでも「どのイベントとして発火するか」によって Order を変えることができるわけです。
というわけで、ADCの優先度は 「Rule x Events のペア単位」で決まるというわけです。
もう少し正確にいうと、「そのルールをどのイベントで発火させるか、その組み合わせに対して Order を設定できる」という言い方になるかと思います。
GTM:優先度の挙動
コードベースで挙動を追ってみましょう。
【GTM】タグ配信の優先度 / タグの順序付け と 非同期処理を待つ実装 でも記載したコードを抜粋します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | var fireTagsForEvent = function (firingMap, eventContext) { var pendingTags = []; // ソート・実行待ちのタグリスト for (var tagIndex = 0; tagIndex < allTagDefinitions.length; tagIndex++) { if (firingMap[tagIndex]) { // このイベントで発火すべきタグか? var tagDef = allTagDefinitions[tagIndex]; // タグの定義オブジェクト var completionCallback = makeCompletionCallback(eventContext.ld); try { // setUp/cleanUp チェーンを含む「実行クロージャ」を組み立てる var executeClosureOrNull = buildExecuteClosure( tagIndex, { onSuccess: completionCallback, onFailure: completionCallback, terminate: completionCallback }, eventContext, tagIndex ); if (executeClosureOrNull) { var tagTypeName = tagDef[Ff.Va]; // タグタイプ名(例: "__html", "__googtag" など) var templateMeta = sandboxedTemplates[tagTypeName]; // テンプレート定義(ない場合は undefined) pendingTags.push({ tagIndex: tagIndex, // ソートの第2キー(同優先度なら定義順) priority: (templateMeta ? templateMeta.priorityOverride || 0 : 0) || getEntityPriority(tagTypeName, 1) || 0, execute: executeClosureOrNull }); } else { // 発火できない状態(ブロック済みなど)→ 完了通知だけ送る reportTagSkipped(tagIndex, eventContext); completionCallback(); } } catch (err) { completionCallback(); } } } pendingTags.sort(comparePriority); // 優先度降順、同値なら tagIndex 昇順 for (var i = 0; i < pendingTags.length; i++) { pendingTags[i].execute(); // 同期的に順番に呼ぶだけ } return pendingTags.length > 0; |
重要なところはここですね
1 2 3 4 | pendingTags.sort(comparePriority); // 優先度降順、同値なら tagIndex 昇順 for (var i = 0; i < pendingTags.length; i++) { pendingTags[i].execute(); // 同期的に順番に呼ぶだけ } |
やっていることは単純で
タグ実行関数の配列を優先度順に並び替えて
それを頭から順番に実行する、というだけです。
ではこのコードは「いつ」実行されるのでしょうか?
簡潔にいいますと「トリガータイミングで毎回タグのソートが走る」です。
(正確には dataLayerの1イベント単位で毎回稼働する)
例えば
「ページビュー」のタイミングになったら、トリガー条件を満たすタグがかき集められる
↓
それらのタグに対して 上記の fireTagsForEvent が実行される
↓
fireTagsForEventの中で sort されて順番にタグ配信される
↓
「DOM Ready」のタイミングになったら、トリガー条件を満たすタグがかき集められる
↓
それらのタグに対して 上記の fireTagsForEvent が実行される
↓
fireTagsForEventの中で sort されて順番にタグ配信される
↓
「ウィンドウの読み込み」タイミングになったら、トリガー条件を満たすタグがかき集められる
↓
それらのタグに対して 上記の fireTagsForEvent が実行される
↓
fireTagsForEventの中で sort されて順番にタグ配信される
・・・のような形で各イベントごとに
イベント発火 → タグ抽出 → ソート処理 → 順次タグ配信(execute)
というのを毎回処理しています。
つまり、GTMはイベントのたび(より正確には event 付きの dataLayer.push が処理されるたび)毎回ルール条件を確認し、「このイベントで発火すべきタグ」を集めて、さらにソートしてからタグ配信、というのを繰り返す仕組みになっています。
もう少し丁寧に表現してみると・・・
前提として、GTM はコンテナ読み込み時点で、タグ・ルール・条件を内部の一覧表として持っている。
dataLayer.push({ event: "..." }) が来ると、その時点の dataLayer の状態を使って「イベント名は一致するか」「URL は条件に合うか」「クリック要素は対象か」などの条件チェックを行い、発火すべきタグを選び、ソートして実行する。
ADC: 優先度の挙動
第1フェーズ
ADCの場合は、ページ読み込み時(launch.js の初期化時)に 1度だけ
全ルールの全イベントが { rule, event} のペアとして展開され
その後 event の Order に従ってイベントタイプを横断してグローバルソートされます。
つまりソート時点では ページビューなのか DOMReadyなのかなどは見ていなく
event の Order だけをみて {rule, event} を並び替えるということです。
具体的には以下のようなことをしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | function buildRuleExecutionOrder(rules) { var pairs = []; // 全ルールのイベントを {rule, event} のペアに展開 rules.forEach(function (rule) { rule.events.forEach(function (event) { pairs.push({ rule: rule, event: event }); // rule: conditions と actions を持つ // event: modulePath と ruleOrder を持つ }); }); // ruleOrder 昇順でソート(イベントタイプ横断のグローバルソート) pairs.sort(function (a, b) { return a.event.ruleOrder - b.event.ruleOrder; }); return pairs;} |
modulePath というのは 「その event がどのタイミング(ページビューなのか DOMReadyなのか?)で発火するのかを教えてくれるもの」と思ってください。
このコードでやっていることを例を使って示します。
まず、以下のように rules の配列があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | var rules = [ { name: "Rule A", events: [ { modulePath: "core/src/lib/events/windowLoaded.js", ruleOrder: 10 } ], conditions: [...], actions: [...] }, { name: "Rule B", events: [ { modulePath: "core/src/lib/events/domReady.js", ruleOrder: 90 } ], conditions: [...], actions: [...] }, { name: "Rule C", events: [ { modulePath: "core/src/lib/events/libraryLoaded.js", // Page Top ruleOrder: 50 } ], conditions: [...], actions: [...] }, { name: "Rule D", events: [ { modulePath: "core/src/lib/events/pageBottom.js", ruleOrder: 20 } ], conditions: [...], actions: [...] }, { name: "Rule E", events: [ { modulePath: "core/src/lib/events/domReady.js", ruleOrder: 5 } ], conditions: [...], actions: [...] } ]; |
これを buildRuleExecutionOrder(rules) が、まず { rule, event } に展開します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | var pairs = [ { rule: RuleA, event: { modulePath: "core/src/lib/events/windowLoaded.js", ruleOrder: 10 } }, { rule: RuleB, event: { modulePath: "core/src/lib/events/domReady.js", ruleOrder: 90 } }, { rule: RuleC, event: { modulePath: "core/src/lib/events/libraryLoaded.js", // Page Top ruleOrder: 50 } }, { rule: RuleD, event: { modulePath: "core/src/lib/events/pageBottom.js", ruleOrder: 20 } }, { rule: RuleE, event: { modulePath: "core/src/lib/events/domReady.js", ruleOrder: 5 } } ] |
その後、pairs.sort でソートした結果が以下です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | [ { rule: RuleE, event: { modulePath: "core/src/lib/events/domReady.js", ruleOrder: 5 } }, { rule: RuleA, event: { modulePath: "core/src/lib/events/windowLoaded.js", ruleOrder: 10 } }, { rule: RuleD, event: { modulePath: "core/src/lib/events/pageBottom.js", ruleOrder: 20 } }, { rule: RuleC, event: { modulePath: "core/src/lib/events/libraryLoaded.js", // Page Top ruleOrder: 50 } }, { rule: RuleB, event: { modulePath: "core/src/lib/events/domReady.js", ruleOrder: 90 } } ] |
先ほど書いたように、イベントタイプを横断して ruleOrder だけをみて並び替えます。
なので、この順序だけ見ると、windowLoaded が pageTop や domReady よりも前にくることも余裕であります。
第2フェーズ
各イベントに対応した Ruleを保持するためのオブジェクトが定義されます。
具体的には pageBottom, DOM Ready, Window Loaded のタイミングで発火するRuleを格納するオブジェクトが作られます。
※pageTop(ページビュー)は保持はされず別処理で queue に入りますがここでは詳細は言及しません。また、DirectCallやClickイベントなども別の処理です。
1 2 3 4 5 | registry = { PAGE_BOTTOM: [], DOM_READY: [], WINDOW_LOADED: []}; |
そして先程ソートした配列が forループ で回されて
各イベントタイプに対応した配列に push されていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | registerPageBottomTrigger: function (trigger) { registry[PAGE_BOTTOM].push({ trigger: trigger });},registerDomReadyTrigger: function (trigger) { registry[DOM_READY].push({ trigger: trigger, syntheticEventFn: createSyntheticEvent.bind(null, document) });},registerWindowLoadedTrigger: function (trigger) { registry[WINDOW_LOADED].push({ trigger: trigger, syntheticEventFn: createSyntheticEvent.bind(null, window) });} |
modulePath が domReady.js であれば、registerDomReadyTrigger が呼ばれます。
modulePath が windowLoaded であれば、registerWindowLoadedTrigger が呼ばれます。
そして例えば DOMReadyであれば
1 2 3 4 | registry[DOM_READY].push({ trigger: trigger, syntheticEventFn: createSyntheticEvent.bind(null, document) }); |
という形で registry[DOM_READY] の配列に push されていくわけです。
trigger というのは複雑なのですが、「イベントが発火したので 『この Rule』 を実行しよう」というのを定義している callback だと思ってください。
syntheticEventFn については eventオブジェクトを作るための関数です。ここでは詳細には踏み込みません。
端的に言えば、registerDomReadyTrigger() は、
DOM Ready 用の registry[DOM_READY] に「あとで Rule を実行するための callback」を順番に push しています。
さて、一連の push が終わったあとどうなっているでしょうか。
ここでは 上記の「trigger」とか「syntheticEventFn」とか「callback」とかは分かりづらいので置いておいて、
シンプルに「どの Ruleがどこの配列に入ったか」を分かりやすく示したいと思います。
前述のように PageTopは registry には保持されなく、queue に積まれます。(詳細は言及しないため無視してもらっても構いません)
処理順 | Rule | イベント | ruleOrder | 登録先 |
|---|---|---|---|---|
1 | RuleE | DOM Ready | 5 |
|
2 | RuleA | Window Loaded | 10 |
|
3 | RuleD | Page Bottom | 20 |
|
4 | RuleC | Page Top / Library Loaded | 50 |
|
5 | RuleB | DOM Ready | 90 |
|
初期化後の状態
保存先 | 中身 |
|---|---|
| RuleE → RuleB |
| RuleA |
| RuleD |
| RuleC(Page Top / Library Loaded) |
さて、ADCの話の一番最初に以下のように記載しました。
ADCの場合は、ページ読み込み時(launch.js の初期化時)に 1度だけ
全ルールの全イベントが { rule, event} のペアとして展開され
その後 event の Order に従ってイベントタイプを横断してグローバルソートされます。
実は、ページ読み込み時(launch.js の初期化時)に、ここまで説明したことが実行されます。
つまり、すべてのRuleに対して、DOMReady で発火する Ruleの配列を registry[DOM_READY] にセット、
WindowLoaded で発火する Ruleの配列をregistry[WINDOW_LOADED]にセット・・・(以下略)
というところまで「初期化の時点」で完了しているわけです。
※厳密には「Rule自体の配列」ではないですが分かりやすく表現しております。
各registry は orderを担保している
この1つ1つの registry[DOM_READY] や registry[WINDOW_LOADED] などが Order を担保していることが分かりますか?
例えば、元々以下のようなRuleの集まりだったとします。
※本来は第1フェーズ、第2フェーズで記載したような オブジェクトの持ち方をしていますが、
わかりやすさ重視で、情報として { rule名, タイミング , order} を記載していると思ってください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [ { ruleA, pageBottom, order: 45 }, { ruleB, windowLoaded, order: 10 }, { ruleC, domReady, order: 55 }, { ruleD, pageBottom, order: 25 }, { ruleE, windowLoaded, order: 60 }, { ruleF, domReady, order: 15 }, { ruleG, pageBottom, order: 50 }, { ruleH, windowLoaded, order: 5 }, { ruleI, domReady, order: 40 }, { ruleJ, pageBottom, order: 30 }, { ruleK, windowLoaded, order: 20 }, { ruleL, domReady, order: 35 }] |
これを Order順でソートするとこうなるのでした。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [ { ruleH, windowLoaded, order: 5 }, { ruleB, windowLoaded, order: 10 }, { ruleF, domReady, order: 15 }, { ruleK, windowLoaded, order: 20 }, { ruleD, pageBottom, order: 25 }, { ruleJ, pageBottom, order: 30 }, { ruleL, domReady, order: 35 }, { ruleI, domReady, order: 40 }, { ruleA, pageBottom, order: 45 }, { ruleG, pageBottom, order: 50 }, { ruleC, domReady, order: 55 }, { ruleE, windowLoaded, order: 60 }] |
この時点で、完全に Order が小さい順に並んでいます。
そして、上記配列を順番に ループを回していって、domReady なら registry[DOM_READY] に pushする、
windowLoaded なら registry[WINDOW_LOADED] に pushする、ということをします。
最初は { ruleH, windowLoaded, order: 5 } なのでregistry[WINDOW_LOADED] = [{ ruleH, order: 5 }]
次は { ruleB, windowLoaded, order: 10 } なのでregistry[WINDOW_LOADED] = [{ ruleH, order: 5 },{ ruleB, order: 10 }]
次は { ruleF, domReady, order: 15 } なのでregistry[DOM_READY] = [{ ruleF, order: 15 }]
次は { ruleK, windowLoaded, order: 20 } なのでregistry[WINDOW_LOADED] = [{ ruleH, order: 5 },{ ruleB, order: 10 },{ ruleK, order: 20 }]
次は { ruleD, pageBottom, order: 25 } なのでregistry[PAGE_BOTTOM] = [{ ruleD, order: 25 }]
次は { ruleJ, pageBottom, order: 30 } なのでregistry[PAGE_BOTTOM] = [{ ruleD, order: 25 },{ ruleJ, order: 30 }]
次は { ruleL, domReady, order: 35 } なのでregistry[DOM_READY] = [{ ruleF, order: 15 },{ ruleL, order: 35 }]
次は { ruleI, domReady, order: 40 } なのでregistry[DOM_READY] = [{ ruleF, order: 15 },{ ruleL, order: 35 },{ ruleI, order: 40 }]
次は { ruleA, pageBottom, order: 45 } なのでregistry[PAGE_BOTTOM] = [{ ruleD, order: 25 },{ ruleJ, order: 30 },{ ruleA, order: 45 }]
次は { ruleG, pageBottom, order: 50 } なのでregistry[PAGE_BOTTOM] = [{ ruleD, order: 25 },{ ruleJ, order: 30 },{ ruleA, order: 45 },{ ruleG, order: 50 }]
次は { ruleC, domReady, order: 55 } なのでregistry[DOM_READY] = [{ ruleF, order: 15 },{ ruleL, order: 35 },{ ruleI, order: 40 },{ ruleC, order: 55 }]
次は { ruleE, windowLoaded, order: 60 } なのでregistry[WINDOW_LOADED] = [{ ruleH, order: 5 },{ ruleB, order: 10 },{ ruleK, order: 20 },{ ruleE, order: 60 }]
そして、最終的には以下のように分かれるわけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | registry = { [WINDOW_LOADED]: [ { ruleH, order: 5 }, { ruleB, order: 10 }, { ruleK, order: 20 }, { ruleE, order: 60 } ], [DOM_READY]: [ { ruleF, order: 15 }, { ruleL, order: 35 }, { ruleI, order: 40 }, { ruleC, order: 55 } ], [PAGE_BOTTOM]: [ { ruleD, order: 25 }, { ruleJ, order: 30 }, { ruleA, order: 45 }, { ruleG, order: 50 } ]} |
見ていただいて分かるように、
registry[DOM_READY] の配列は order順に並んでいますよね?(15→35→40→55)
registry[WINDOW_LOADED] も 5→10→20→60 と order順です。
pageBottom も同様です。
考えてみれば当たり前なのですが、元々の配列が
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [ { ruleH, windowLoaded, order: 5 }, { ruleB, windowLoaded, order: 10 }, { ruleF, domReady, order: 15 }, { ruleK, windowLoaded, order: 20 }, { ruleD, pageBottom, order: 25 }, { ruleJ, pageBottom, order: 30 }, { ruleL, domReady, order: 35 }, { ruleI, domReady, order: 40 }, { ruleA, pageBottom, order: 45 }, { ruleG, pageBottom, order: 50 }, { ruleC, domReady, order: 55 }, { ruleE, windowLoaded, order: 60 }] |
と完全に order 順になっていて、それを forループで回して
先頭から処理していくのですから(つまり order順に処理していくわけなので)
同じ domReady であれば 15→ 35 → 40 → 55 と order順に pushされていく = orderは担保された状態で維持される、というわけです。
Ruleの発火
Ruleの発火については非常にシンプルで
例えば DOMReady のタイミングになったら
先ほどの registry[DOM_READY] の配列をループさせて、先頭から順番に実行していく、ということになります。
registry[DOM_READY] の配列が既に Order順に並んでいるわけですから、
先頭から順番に処理していけば、それがOrder(優先度)を加味した処理順ということになります。
同様に、WindowLoaded のタイミングであれば、registry[WINDOW_LOADED] の配列をループさせて順番に実行していきます。
GTMとADCの異なる点
GTMのpriority と ADC の Order を差異を
まとめると以下のような感じになるかと思います。
GTM priority | ADC Order | |
|---|---|---|
ソートのタイミング | イベントが起こるたびに毎回、発火対象のタグを並べる | ページロード時に1回だけ、全 rule event を並べる |
並べる基準 | タグに設定された priority | event に設定された |
数値の意味 | 値が大きいほど先に実行される | 値が小さいほど先に登録される |
対象単位 | タグ単位 | rule 単位ではなく、rule に紐づく event 単位 |
発火時の処理 | 発火時に対象タグを priority 順に並べて実行する | 発火時には基本的に再ソートせず、ページロード時に登録された順番で実行する |
まとめ
いかがでしたでしょうか。
簡単ではありますが、GTMの優先度(priority)と ADCの優先度(Order)の仕組み上の違いについて記載しました。
大きな違いとしてはやはり「ソートのタイミング」が挙げられるかと思います。
GTMの方はイベントがトリガーされる度に、発火させるタグを集めて、ソートして、順次実行する、というのを毎回行っています。
ADCの場合は、ページロード時(launch.jsの初期化時)に一度だけ、すべての{Rule, Event} のペアをソートして、各イベントタイプごとの配列にPushしていきます。
そして、例えば、実際に DOMReadyのタイミングになったら、DOMReady用の配列から順番に取り出して実行していく、ということになります。
パフォーマンス面の違い
パフォーマンスだけを見ると、構造上は、ADCの方が発火時の処理範囲を絞りやすいです。
GTM | ADC | |
|---|---|---|
発火時に見る範囲 | event ごとにルール表を見直す | そのイベントタイプに登録済みの trigger が中心 |
ソート | event ごとに発火候補タグを sort | 初期化時に sort 済み。発火時は再 sort しない |
条件評価 | event ごとに必要な条件を評価 | trigger 実行時に rule conditions を評価 |
実用上の差 | 通常は体感しにくい | 通常は体感しにくい |
ただし、この違いは多くのサイトでは体感差にはなりにくいと思います。
タグ数や条件数が極端に多い場合を除けば、設計上・構造上の実行速度の体感差はほとんどないと思います。
以上です。
これが分かったらかなんだ?という話は特になく
動きとしてどう差異があるのだろうというのを調べてみた記事でした。
何かの参考になれば!
Bye, bye.





