code: def create_dataloader(
      graph,
      features,
      itemset,
      batch_size,
      fanout,
      device,
      num_workers,
      job,
  ):
      """
      [HIGHLIGHT]
      Get a GraphBolt version of a dataloader for node classification tasks.
      This function demonstrates how to utilize functional forms of datapipes in
      GraphBolt. For a more detailed tutorial, please read the examples in
      `dgl/notebooks/graphbolt/walkthrough.ipynb`.
      Alternatively, you can create a datapipe using its class constructor.
  
      Parameters
      ----------
      job : one of ["train", "evaluate", "infer"]
          The stage where dataloader is created, with options "train", "evaluate"
          and "infer".
      Other parameters are explicated in the comments below.
      """
  
      ############################################################################
      # [Step-1]:
      # gb.ItemSampler()
      # [Input]:
      # 'itemset': The current dataset. (e.g. `train_set` or `valid_set`)
      # 'batch_size': Specify the number of samples to be processed together,
      # referred to as a 'mini-batch'. (The term 'mini-batch' is used here to
      # indicate a subset of the entire dataset that is processed together. This
      # is in contrast to processing the entire dataset, known as a 'full batch'.)
      # 'job': Determines whether data should be shuffled. (Shuffling is
      # generally used only in training to improve model generalization. It's
      # not used in validation and testing as the focus there is to evaluate
      # performance rather than to learn from the data.)
      # [Output]:
      # An ItemSampler object for handling mini-batch sampling.
      # [Role]:
      # Initialize the ItemSampler to sample mini-batche from the dataset.
      ############################################################################
      datapipe = gb.ItemSampler(
          itemset, batch_size=batch_size, shuffle=(job == "train")
      )
  
      ############################################################################
      # [Step-2]:
      # self.copy_to()
      # [Input]:
      # 'device': The device to copy the data to.
      # 'extra_attrs': The extra attributes to copy.
      # [Output]:
      # A CopyTo object to copy the data to the specified device. Copying here
      # ensures that the rest of the operations run on the GPU.
      ############################################################################
      if args.storage_device != "cpu":
          datapipe = datapipe.copy_to(
              device=device, extra_attrs=["seed_nodes"]
          )
  
      ############################################################################
      # [Step-3]:
      # self.sample_neighbor()
      # [Input]:
      # 'graph': The network topology for sampling.
      # '[-1] or fanout': Number of neighbors to sample per node. In
      # training or validation, the length of `fanout` should be equal to the
      # number of layers in the model. In inference, this parameter is set to
      # [-1], indicating that all neighbors of a node are sampled.
      # [Output]:
      # A NeighborSampler object to sample neighbors.
      # [Role]:
      # Initialize a neighbor sampler for sampling the neighborhoods of nodes.
      ############################################################################
      datapipe = datapipe.sample_neighbor(
          graph, fanout if job != "infer" else [-1]
      )
  
      ############################################################################
      # [Step-4]:
      # self.fetch_feature()
      # [Input]:
      # 'features': The node features.
      # 'node_feature_keys': The keys of the node features to be fetched.
      # [Output]:
      # A FeatureFetcher object to fetch node features.
      # [Role]:
      # Initialize a feature fetcher for fetching features of the sampled
      # subgraphs. This step is skipped in inference because features are updated
      # as a whole during it, thus storing features in minibatch is unnecessary.
      ############################################################################
      if job != "infer":
          datapipe = datapipe.fetch_feature(
              features, node_feature_keys=["feat"]
          )
  
      ############################################################################
      # [Step-5]:
      # self.copy_to()
      # [Input]:
      # 'device': The device to copy the data to.
      # [Output]:
      # A CopyTo object to copy the data to the specified device.
      ############################################################################
      if args.storage_device == "cpu":
          datapipe = datapipe.copy_to(device=device)
  
      ############################################################################
      # [Step-6]:
      # gb.DataLoader()
      # [Input]:
      # 'datapipe': The datapipe object to be used for data loading.
      # 'num_workers': The number of processes to be used for data loading.
      # [Output]:
      # A DataLoader object to handle data loading.
      # [Role]:
      # Initialize a multi-process dataloader to load the data in parallel.
      ############################################################################
      dataloader = gb.DataLoader(datapipe, num_workers=num_workers)
  
      # Return the fully-initialized DataLoader object.
      return dataloader
  
imports: import argparse
  import time
  import dgl.graphbolt
  import dgl.nn
  import torch
  import torch.nn
  import torch.nn.functional
  import torchmetrics.functional
  from tqdm import tqdm
label_desc: Use image node_id 1 for calling a global function with example usage: create_dataloader(graph, features, itemset, batch_size, fanout, device, num_workers, job) and returns: dataloader