Boby
Boby

Reputation: 1202

Run "future" one by one

now i have this script

  Future<List<HotProducts>> _loadHotProducts;
  Future<List<NewProducts>> _loadNewProducts;
  Future<List<Promotion>> _loadPromotions;
  Future<List<LatestSoldItem>> _loadLatestSoldItem;
    setState(() {
              userId = result;
              _loadHotProducts = HotProductsApiClient().fetchHotProducts(http.Client(), userId);
              _loadNewProducts = NewProductApiClient().fetchNewProducts(http.Client(), userId);
              _loadPromotions = PromotionApiClient().fetchPromotion(http.Client());
              _loadLatestSoldItem = LatestSoldApiClient().fetchProducts(http.Client(), userId);
            });

all my script above is about calling data from my server. Now, i want to run it one by one. So, i'm trying this way

_loadHotProducts = HotProductsApiClient().fetchHotProducts(http.Client(), userId).then((_){
            _loadNewProducts = NewProductApiClient().fetchNewProducts(http.Client(), userId);
          });

I get this errors

The getter 'length' was called on null.
Receiver: null
Tried calling: length

How can i fix it ? or is there any better way ?

Upvotes: 0

Views: 432

Answers (3)

chunhunghan
chunhunghan

Reputation: 54377

The problem you encounter is inside your fetch function
you have to await all your http and future job
You can copy paste run correct and problem code in DartPad
The following demo have 3 layer function call, you check effect with or without await
When you doubt, just print message and you will see execution sequence
The following correct and problem is provided as below

full code correct

import 'dart:async';
import 'dart:math';


Future<String> fetchHotProductsLongJob() async {
  await Future.delayed(const Duration(seconds: 10), () {
    print('fetchHotProductsLongJob then');
  });
  print("fetchHotProductsLongJob done");
  return "abc";

}

Future<String> fetchHotProducts() async {
  await Future.delayed(const Duration(seconds: 7), () {
    print("fetchHotProducts then");
  });
  await fetchHotProductsLongJob();
  print("fetchHotProducts done");
  return "abc";

}

Future<String> fetchNewProductsLongJob() async {
  await Future.delayed(const Duration(seconds: 10), () {
    print('fetchNewProductsLongJob then');
  });
  print("fetchNewProductsLongJob done");
  return "abc";

}

Future<String> fetchNewProducts() async {
  await Future.delayed(const Duration(seconds: 3), () async{
    print("fetchNewProducts then");
    await fetchNewProductsLongJob();
  });
  print("fetchNewProducts done");
  return "abc";

}



void main()  async{
  print("fetchHotProducts job start");
  var Job1 = await fetchHotProducts();  
  print("fetchHotProducts job done");

  print("fetchNewProducts job start");
  var Job2 = await fetchNewProducts();
  print("fetchNewProducts job done");  

}

correct output

fetchHotProducts job start
fetchHotProducts then
fetchHotProductsLongJob then
fetchHotProductsLongJob done
fetchHotProducts done
fetchHotProducts job done
fetchNewProducts job start
fetchNewProducts then
fetchNewProductsLongJob then
fetchNewProductsLongJob done
fetchNewProducts done
fetchNewProducts job done

correct demo enter image description here

problem code after remove some await

import 'dart:async';
import 'dart:math';


Future<String> fetchHotProductsLongJob() async {
  Future.delayed(const Duration(seconds: 10), () {
    print('fetchHotProductsLongJob then');
  });
  print("fetchHotProductsLongJob done");
  return "abc";

}

Future<String> fetchHotProducts() async {
  Future.delayed(const Duration(seconds: 7), () {
    print("fetchHotProducts then");
  });
  fetchHotProductsLongJob();
  print("fetchHotProducts done");
  return "abc";

}

Future<String> fetchNewProductsLongJob() async {
  await Future.delayed(const Duration(seconds: 10), () {
    print('fetchNewProductsLongJob then');
  });
  print("fetchNewProductsLongJob done");
  return "abc";

}

Future<String> fetchNewProducts() async {
  await Future.delayed(const Duration(seconds: 3), () async{
    print("fetchNewProducts then");
    await fetchNewProductsLongJob();
  });
  print("fetchNewProducts done");
  return "abc";

}



void main()  async{
  print("fetchHotProducts job start");
  var Job1 = await fetchHotProducts();  
  print("fetchHotProducts job done");

  print("fetchNewProducts job start");
  var Job2 = await fetchNewProducts();
  print("fetchNewProducts job done");  

}

problem code output
you can see fetchHotProducts job still not finish

fetchHotProducts job start
fetchHotProductsLongJob done
fetchHotProducts done
fetchHotProducts job done
fetchNewProducts job start
fetchNewProducts then
    fetchHotProducts then
    fetchHotProductsLongJob then
fetchNewProductsLongJob then
fetchNewProductsLongJob done
fetchNewProducts done
fetchNewProducts job done

Upvotes: 1

Mohamad Assem Nasser
Mohamad Assem Nasser

Reputation: 1109

await is meant to interrupt the process flow until the async method has finished. then however does not interrupt the process flow (meaning the next instructions will be executed) but enables you to run code when the async method is finished.

In your example, you cannot achieve what you want when you use then because the code is not waiting and the return statement is processed and thus returns an empty List.

When you add the await, you explicitly say: don't go further until my Future is completed.

_loadHotProducts = await HotProductsApiClient().fetchHotProducts(http.Client(), userId);
_loadNewProducts = await NewProductApiClient().fetchNewProducts(http.Client(), userId);
_loadPromotions = await PromotionApiClient().fetchPromotion(http.Client());
_loadLatestSoldItem = await LatestSoldApiClient().fetchProducts(http.Client(), userId);

Using await directly in the setState won't work, since setState function isn't async and it is not recommended. Just wrap them with an async function that will be called in the setState method. So the final result should be something like this:

Future<List<HotProducts>> _loadHotProducts;
Future<List<NewProducts>> _loadNewProducts;
Future<List<Promotion>> _loadPromotions;
Future<List<LatestSoldItem>> _loadLatestSoldItem;
Future load() async {
  userId = result;
  _loadHotProducts = await HotProductsApiClient().fetchHotProducts(http.Client(), userId);
  _loadNewProducts = await NewProductApiClient().fetchNewProducts(http.Client(), userId);
  _loadPromotions = await PromotionApiClient().fetchPromotion(http.Client());
  _loadLatestSoldItem = await LatestSoldApiClient().fetchProducts(http.Client(), userId);
  }
setState(() {
  load();
});

Upvotes: 1

Karim
Karim

Reputation: 1010

you could use await

Future<List<HotProducts>> _loadHotProducts;
Future<List<NewProducts>> _loadNewProducts;
Future<List<Promotion>> _loadPromotions;
Future<List<LatestSoldItem>> await _loadLatestSoldItem;
userId = result;

_loadHotProducts = await HotProductsApiClient().fetchHotProducts(http.Client(), userId);
_loadNewProducts = await NewProductApiClient().fetchNewProducts(http.Client(), userId);
_loadPromotions = await PromotionApiClient().fetchPromotion(http.Client());
_loadLatestSoldItem = await LatestSoldApiClient().fetchProducts(http.Client(), userId);

setState(() {});

You will need to make the method async.

Upvotes: 0

Related Questions