Skip to content

Commit 1d52be6

Browse files
committed
add new publisher
Corresponds to graphql/graphql-js#3786
1 parent db5742c commit 1d52be6

File tree

1 file changed

+159
-97
lines changed

1 file changed

+159
-97
lines changed

spec/Section 6 -- Execution.md

Lines changed: 159 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -361,32 +361,87 @@ The Async Payload Records may be published lazily as requested, with the
361361
internal state of the unpublished stream held by a Publisher Record unique to
362362
the request.
363363

364-
- {subsequentPayloads}: the set of Async Payload Records for this response that
365-
have not yet been published.
364+
- {pending}: the set of Async Payload Records for this response that have not
365+
yet been completed.
366+
- {waiting}: the set of Async Payload Records for this response that have been
367+
completed, but are waiting for a parent to complete.
368+
- {waitingByParent}: an unordered map of uncompleted parent Async Payload
369+
Records to sets of completed child Async Payload Records.
370+
- {pushed}: a weakly held set of Async Payload Records for this response that
371+
have been completed, but are waiting for a parent to complete.
372+
- {current}: the set of Async Payload Records for this response that may be
373+
yielded on the next request.
374+
- {signal}: An asynchronous signal that can be awaited and triggered.
366375

367376
## Create Publisher
368377

369378
CreatePublisher():
370379

371380
- Let {publisherRecord} be a publisher record.
372-
- Initialize {subsequentPayloads} on {publisherRecord} to an empty set.
381+
- Initialize {pending} on {publisherRecord} to an empty set.
382+
- Initialize {waiting} on {publisherRecord} to an empty set.
383+
- Initialize {waitingByParent} on {publisherRecord} to an empty unordered map.
384+
- Initialize {pushed} on {publisherRecord} to an empty set.
385+
- Initialize {current} on {publisherRecord} to an empty set.
386+
- Initialize {signal}.
373387
- Return {publisherRecord}.
374388

375389
## Has Subsequent Payloads
376390

377391
HasSubsequentPayloads(publisherRecord):
378392

379-
- Let {subsequentPayloads} be the corresponding entry on {publisherRecord}.
380-
- Let {size} be the number of payloads within {subsequentPayloads}.
393+
- Let {pending} be the corresponding entry on {publisherRecord}.
394+
- Let {size} be the number of payloads within {pending}.
395+
- If {size} is greater than zero, return {true}.
396+
- Let {waiting} be the corresponding entry on {publisherRecord}.
397+
- Let {size} be the number of payloads within {waiting}.
398+
- If {size} is greater than zero, return {true}.
399+
- Let {current} be the corresponding entry on {publisherRecord}.
400+
- Let {size} be the number of payloads within {current}.
381401
- If {size} is greater than zero, return {true}.
382402
- Return {false}.
383403

384404
## Add Payload
385405

386406
AddPayload(payload, publisherRecord):
387407

388-
- Let {subsequentPayloads} be the corresponding entry on {publisherRecord}.
389-
- Add {payload} to {subsequentPayloads}.
408+
- Let {pending} be the corresponding entry on {publisherRecord}.
409+
- Add {payload} to {pending}.
410+
411+
## Complete Payload
412+
413+
CompletePayload(payload, publisherRecord):
414+
415+
- Let {pending} be the corresponding entry on {publisherRecord}.
416+
- If {payload} is not within {pending}, return.
417+
- Remove {payload} from {pending}.
418+
- Let {parentContext} be the corresponding entry on {payload}.
419+
- If {parentContext} is not defined:
420+
- Call PushPayload(payload, publisherRecord).
421+
- Let {signal} be the corresponding entry on {publisherRecord}.
422+
- Trigger {signal}.
423+
- Otherwise:
424+
- Let {waiting} and {waitingByChildren} be the corresponding entries on
425+
{publisherRecord}.
426+
- Add {payload} to {waiting}.
427+
- Let {children} be the set in {waitingByParent} for {parentContext}; if no
428+
such set exists, create it as an empty set.
429+
- Append {payload} to {children}.
430+
431+
## Push Payload
432+
433+
PushPayload(payload, publisherRecord):
434+
435+
- Let {pushed}, {current}, and {waitingByParent} be the corresponding entries on
436+
{publisherRecord}.
437+
- Add {payload} to {pushed} and {current}.
438+
- Let {children} be the set in {waitingByParent} for {parentContext}.
439+
- If {children} is not defined, return.
440+
- Let {waiting} be the corresponding entry on {publisherRecord}.
441+
- For each {child} in {children}:
442+
- Call {PushPayload(payload, publisherRecord)}.
443+
- Remove {child} from {waiting}.
444+
- Remove the set in {waitingByParent} for {parentContext}.
390445

391446
## Yield Subsequent Payloads
392447

@@ -396,15 +451,13 @@ payloads should be processed.
396451

397452
YieldSubsequentPayloads(initialResponse, publisherRecord):
398453

399-
- Let {subsequentPayloads} be the corresponding entry on {publisherRecord}.
400-
- Let {initialRecords} be any items in {subsequentPayloads} with a completed
401-
{dataExecution}.
454+
- Let {current} be the corresponding entry on {publisherRecord}.
402455
- Initialize {initialIncremental} to an empty list.
403-
- For each {record} in {initialRecords}:
404-
- Remove {record} from {subsequentPayloads}.
456+
- For each {record} in {current}:
457+
- Remove {record} from {current}.
405458
- If {isCompletedIterator} on {record} is {true}:
406459
- Continue to the next record in {records}.
407-
- Let {payload} be the completed result returned by {dataExecution}.
460+
- Let {payload} be the corresponding entry on {record}.
408461
- Append {payload} to {initialIncremental}.
409462
- If {initialIncremental} is not empty:
410463
- Add an entry to {initialResponse} named `incremental` containing the value
@@ -416,17 +469,17 @@ YieldSubsequentPayloads(initialResponse, publisherRecord):
416469
- If {record} contains {iterator}:
417470
- Send a termination signal to {iterator}.
418471
- Return.
419-
- Wait for at least one record in {subsequentPayloads} to have a completed
420-
{dataExecution}.
472+
- Let {signal} be the corresponding entry on {publisherRecord}.
473+
- Wait for {signal} to be triggered.
474+
- Reinitialize {signal} on {publisherRecord}.
421475
- Let {subsequentResponse} be an unordered map with an entry {incremental}
422476
initialized to an empty list.
423-
- Let {records} be the items in {subsequentPayloads} with a completed
424-
{dataExecution}.
425-
- For each {record} in {records}:
477+
- Let {current} be the corresponding entry on {publisherRecord}.
478+
- For each {record} in {current}:
426479
- Remove {record} from {subsequentPayloads}.
427480
- If {isCompletedIterator} on {record} is {true}:
428481
- Continue to the next record in {records}.
429-
- Let {payload} be the completed result returned by {dataExecution}.
482+
- Let {payload} be the corresponding entry on {record}.
430483
- Append {payload} to the {incremental} entry on {subsequentResponse}.
431484
- If {subsequentPayloads} is empty:
432485
- Add an entry to {subsequentResponse} named `hasNext` with the value
@@ -505,20 +558,34 @@ outside of {ExecuteDeferredFragment} or {ExecuteStreamField}.
505558

506559
FilterSubsequentPayloads(publisherRecord, nullPath, currentAsyncRecord):
507560

508-
- Let {subsequentPayloads} be the corresponding entry on {publisherRecord}.
509-
- For each {asyncRecord} in {subsequentPayloads}:
510-
- If {asyncRecord} is the same record as {currentAsyncRecord}:
511-
- Continue to the next record in {subsequentPayloads}.
512-
- Initialize {index} to zero.
513-
- While {index} is less then the length of {nullPath}:
514-
- Initialize {nullPathItem} to the element at {index} in {nullPath}.
515-
- Initialize {asyncRecordPathItem} to the element at {index} in the {path}
516-
of {asyncRecord}.
517-
- If {nullPathItem} is not equivalent to {asyncRecordPathItem}:
518-
- Continue to the next record in {subsequentPayloads}.
519-
- Increment {index} by one.
520-
- Remove {asyncRecord} from {subsequentPayloads}. Optionally, cancel any
521-
incomplete work in the execution of {asyncRecord}.
561+
- Let {pending}, {current}, {waiting}, and {waitingByParent} be the
562+
corresponding entries on {publisherRecord}.
563+
- For each {asyncRecord} in {pending} and {current}:
564+
- If {ShouldKeepPayload(asyncRecord, nullPath, currentAsyncRecord)} is {true}:
565+
- Continue to the next record in {set}.
566+
- Remove {asyncRecord} from {set}. Optionally, cancel any incomplete work in
567+
the execution of {asyncRecord}.
568+
- For each {asyncRecord} in {waiting}:
569+
- If {ShouldKeepPayload(asyncRecord, nullPath, currentAsyncRecord)} is {true}:
570+
- Continue to the next record in {waiting}.
571+
- Remove {asyncRecord} from {waiting}. Optionally, cancel any incomplete work
572+
in the execution of {asyncRecord}.
573+
- Let {parentContext} be the corresponding entry on {asyncRecord}.
574+
- Let {children} be the set in {waitingByParent} for {parentContext}.
575+
- Remove {asyncRecord} from {children}.
576+
577+
ShouldKeepPayload(asyncRecord, nullPath, currentAsyncRecord):
578+
579+
- If {asyncRecord} is the same record as {currentAsyncRecord}:
580+
- Return {true}.
581+
- Initialize {index} to zero.
582+
- While {index} is less then the length of {nullPath}:
583+
- Initialize {nullPathItem} to the element at {index} in {nullPath}.
584+
- Initialize {asyncRecordPathItem} to the element at {index} in the {path} of
585+
{asyncRecord}.
586+
- If {nullPathItem} is not equivalent to {asyncRecordPathItem}:
587+
- Return {true}.
588+
- Increment {index} by one. Return {false}.
522589

523590
For example, assume the field `alwaysThrows` is a `Non-Null` type that always
524591
raises a field error:
@@ -797,51 +864,49 @@ DoesFragmentTypeApply(objectType, fragmentType):
797864
An Async Payload Record is either a Deferred Fragment Record or a Stream Record.
798865
All Async Payload Records are structures containing:
799866

867+
- {parentRecord}: The generating parent Async Payload Record, not defined if
868+
this Async Payload Record is spawned by the initial result.
800869
- {label}: value derived from the corresponding `@defer` or `@stream` directive.
801870
- {path}: a list of field names and indices from root to the location of the
802871
corresponding `@defer` or `@stream` directive.
803872
- {iterator}: The underlying iterator if created from a `@stream` directive.
804873
- {isCompletedIterator}: a boolean indicating the payload record was generated
805874
from an iterator that has completed.
806875
- {errors}: a list of field errors encountered during execution.
807-
- {dataExecution}: A result that can notify when the corresponding execution has
808-
completed.
876+
- {payload}: An unordered map containing the formatted payload.
809877

810878
#### Execute Deferred Fragment
811879

812880
ExecuteDeferredFragment(label, objectType, objectValue, groupedFieldSet, path,
813881
variableValues, parentRecord, publisherRecord):
814882

815-
- Let {deferRecord} be an async payload record created from {label} and {path}.
816-
- Initialize {errors} on {deferRecord} to an empty list.
817-
- Let {dataExecution} be the asynchronous future value of:
818-
- Let {payload} be an unordered map.
819-
- Initialize {resultMap} to an empty ordered map.
820-
- For each {groupedFieldSet} as {responseKey} and {fields}:
821-
- Let {fieldName} be the name of the first entry in {fields}. Note: This
822-
value is unaffected if an alias is used.
823-
- Let {fieldType} be the return type defined for the field {fieldName} of
824-
{objectType}.
825-
- If {fieldType} is defined:
826-
- Let {responseValue} be {ExecuteField(objectType, objectValue, fieldType,
827-
fields, variableValues, path, publisherRecord, asyncRecord)}.
828-
- Set {responseValue} as the value for {responseKey} in {resultMap}.
829-
- Append any encountered field errors to {errors}.
830-
- If {parentRecord} is defined:
831-
- Wait for the result of {dataExecution} on {parentRecord}.
832-
- If {errors} is not empty:
833-
- Add an entry to {payload} named `errors` with the value {errors}.
834-
- If a field error was raised, causing a {null} to be propagated to
835-
{responseValue}:
836-
- Add an entry to {payload} named `data` with the value {null}.
837-
- Otherwise:
838-
- Add an entry to {payload} named `data` with the value {resultMap}.
839-
- If {label} is defined:
840-
- Add an entry to {payload} named `label` with the value {label}.
841-
- Add an entry to {payload} named `path` with the value {path}.
842-
- Return {payload}.
843-
- Set {dataExecution} on {deferredFragmentRecord}.
883+
- Let {deferRecord} be an async payload record created from {parentRecord},
884+
{label}, and {path}.
844885
- Call {AddPayload(deferRecord, publisherRecord)}.
886+
- Initialize {errors} on {deferRecord} to an empty list.
887+
- Initialize {resultMap} to an empty ordered map.
888+
- For each {groupedFieldSet} as {responseKey} and {fields}:
889+
- Let {fieldName} be the name of the first entry in {fields}. Note: This value
890+
is unaffected if an alias is used.
891+
- Let {fieldType} be the return type defined for the field {fieldName} of
892+
{objectType}.
893+
- If {fieldType} is defined:
894+
- Let {responseValue} be {ExecuteField(objectType, objectValue, fieldType,
895+
fields, variableValues, path, publisherRecord, asyncRecord)}.
896+
- Set {responseValue} as the value for {responseKey} in {resultMap}.
897+
- Append any encountered field errors to {errors}.
898+
- If {errors} is not empty:
899+
- Add an entry to {payload} named `errors` with the value {errors}.
900+
- If a field error was raised, causing a {null} to be propagated to
901+
{responseValue}:
902+
- Add an entry to {payload} named `data` with the value {null}.
903+
- Otherwise:
904+
- Add an entry to {payload} named `data` with the value {resultMap}.
905+
- If {label} is defined:
906+
- Add an entry to {payload} named `label` with the value {label}.
907+
- Add an entry to {payload} named `path` with the value {path}.
908+
- Set {payload} on {deferRecord}.
909+
- Call {CompletePayload(payload, publisherRecord)}.
845910

846911
## Executing Fields
847912

@@ -968,43 +1033,40 @@ yielded items satisfies `initialCount` specified on the `@stream` directive.
9681033
ExecuteStreamField(label, iterator, index, fields, innerType, path,
9691034
streamRecord, variableValues, publisherRecord):
9701035

971-
- Let {streamRecord} be an async payload record created from {label}, {path},
972-
and {iterator}.
1036+
- Let {streamRecord} be an async payload record created from {parentRecord},
1037+
{label}, {path}, and {iterator}.
1038+
- Call {AddPayload(streamRecord, publisherRecord)}.
9731039
- Initialize {errors} on {streamRecord} to an empty list.
9741040
- Let {itemPath} be {path} with {index} appended.
975-
- Let {dataExecution} be the asynchronous future value of:
976-
- Wait for the next item from {iterator}.
977-
- If an item is not retrieved because {iterator} has completed:
978-
- Set {isCompletedIterator} to {true} on {streamRecord}.
979-
- Return {null}.
980-
- Let {payload} be an unordered map.
981-
- If an item is not retrieved because of an error:
982-
- Append the encountered error to {errors}.
1041+
- Wait for the next item from {iterator}.
1042+
- If an item is not retrieved because {iterator} has completed:
1043+
- Set {isCompletedIterator} to {true} on {streamRecord}.
1044+
- Return {null}.
1045+
- Let {payload} be an unordered map.
1046+
- If an item is not retrieved because of an error:
1047+
- Append the encountered error to {errors}.
1048+
- Add an entry to {payload} named `items` with the value {null}.
1049+
- Otherwise:
1050+
- Let {item} be the item retrieved from {iterator}.
1051+
- Let {data} be the result of calling {CompleteValue(innerType, fields, item,
1052+
variableValues, itemPath, publisherRecord, parentRecord)}.
1053+
- Append any encountered field errors to {errors}.
1054+
- Increment {index}.
1055+
- Call {ExecuteStreamField(label, iterator, index, fields, innerType, path,
1056+
streamRecord, variableValues, publisherRecord)}.
1057+
- If a field error was raised, causing a {null} to be propagated to {data},
1058+
and {innerType} is a Non-Nullable type:
9831059
- Add an entry to {payload} named `items` with the value {null}.
9841060
- Otherwise:
985-
- Let {item} be the item retrieved from {iterator}.
986-
- Let {data} be the result of calling {CompleteValue(innerType, fields,
987-
item, variableValues, itemPath, publisherRecord, parentRecord)}.
988-
- Append any encountered field errors to {errors}.
989-
- Increment {index}.
990-
- Call {ExecuteStreamField(label, iterator, index, fields, innerType, path,
991-
streamRecord, variableValues, publisherRecord)}.
992-
- If a field error was raised, causing a {null} to be propagated to {data},
993-
and {innerType} is a Non-Nullable type:
994-
- Add an entry to {payload} named `items` with the value {null}.
995-
- Otherwise:
996-
- Add an entry to {payload} named `items` with a list containing the value
997-
{data}.
998-
- If {errors} is not empty:
999-
- Add an entry to {payload} named `errors` with the value {errors}.
1000-
- If {label} is defined:
1001-
- Add an entry to {payload} named `label` with the value {label}.
1002-
- Add an entry to {payload} named `path` with the value {itemPath}.
1003-
- If {parentRecord} is defined:
1004-
- Wait for the result of {dataExecution} on {parentRecord}.
1005-
- Return {payload}.
1006-
- Set {dataExecution} on {streamRecord}.
1007-
- Call {AddPayload(streamRecord, publisherRecord)}.
1061+
- Add an entry to {payload} named `items` with a list containing the value
1062+
{data}.
1063+
- If {errors} is not empty:
1064+
- Add an entry to {payload} named `errors` with the value {errors}.
1065+
- If {label} is defined:
1066+
- Add an entry to {payload} named `label` with the value {label}.
1067+
- Add an entry to {payload} named `path` with the value {itemPath}.
1068+
- Set {payload} on {streamRecord}.
1069+
- Call {CompletePayload(streamRecord, publisherRecord)}.
10081070

10091071
CompleteValue(fieldType, fields, result, variableValues, path, publisherRecord,
10101072
asyncRecord):

0 commit comments

Comments
 (0)