duguyihou / react-native-turbo-image

Performant image component for React Native
https://www.npmjs.com/package/react-native-turbo-image
MIT License
165 stars 8 forks source link

feature: Instant Resolution for Already Fetched Images in Prefetch #345

Closed gronxb closed 2 months ago

gronxb commented 2 months ago

What feature or enhancement are you suggesting?

Thank you for creating such a great library.

In the case of prefetch, it makes a request every time it’s called. However, is it possible to immediately resolve for images that have already been successfully fetched?

const result = await TurboImage.prefetch([{
   uri: "image",
}]); // result returns true, but it takes some time.

const result2 = await TurboImage.prefetch([{
   uri: "image",
}]); // result should immediately return true since the image was successfully fetched before.

What Platforms whould this feature/enhancement affect?

iOS, Android

Alternatives/Workarounds

Example expo-image: image

X

Gautham495 commented 2 months ago

Are you seeing images are being fetched again even though they are fetched before? I am seeing it constantly in both platforms.

gronxb commented 2 months ago

@Gautham495

https://github.com/duguyihou/react-native-turbo-image/blob/main/ios/TurboImageViewManager.swift#L21-L44

When looking at the code, I expect that if prefetch is triggered twice for the same source, it will send two network requests. (If that’s not the case, please correct me.)

In my use case, prefetch is called repeatedly because the component performs the prefetch and waits. However, once an image is fetched, it should immediately return a promise indicating that the loading has already been completed.

Separate from the two network requests, as you mentioned, the image will still be displayed correctly.

Gautham495 commented 2 months ago

I am seeing condition where if i move from one screen to another without the image being fully loaded from the remote source, it just gets stuck at the placeholder blurhash. Where you getting this as well?

duguyihou commented 2 months ago

@gronxb

I only see network requests with prefetch one time.

https://github.com/user-attachments/assets/ea6aa54a-78d1-49c8-8383-afc3e35ab3f1

https://github.com/user-attachments/assets/f12e0327-8df9-456c-82ca-7154dde9506c

gronxb commented 2 months ago

@duguyihou Thank you for your quick response, and I appreciate you confirming that there is only one network request.

When I tested it, the following behavior occurred: With the below code, I'm checking how long the prefetch promise takes to resolve.

performance check code:

// HomeScreen.tsx
const LeftButton = () => {
  const handlePrefetch = () => {
    Alert.alert('prefetch', '', [
      {
        text: 'Prefetch with urlCache',
        onPress: async () => {
          const startTime = performance.now(); // 시간 측정 시작
          const result = await TurboImage.prefetch(prefetchWithUrlCacheData);
          const endTime = performance.now(); // 시간 측정 종료

          console.log(
            'Time for prefetchWithUrlCacheData:',
            `${endTime - startTime}ms`
          );
          console.log('prefetchWithUrlCacheData', result);
        },
        style: 'default',
      },
      {
        text: 'Prefetch with dataCache',
        onPress: async () => {
          const startTime = performance.now(); // 시간 측정 시작
          const result = await TurboImage.prefetch(
            PrefetchWithDataCacheData,
            'dataCache'
          );
          const endTime = performance.now(); // 시간 측정 종료

          console.log(
            'Time for prefetchWithUrlCacheData:',
            `${endTime - startTime}ms`
          );
          console.log('prefetchWithUrlCacheData', result);
        },
        style: 'default',
      },
      {
        text: 'Cancel',
        style: 'cancel',
      },
    ]);
  };
  return <Button title="Prefetch" onPress={handlePrefetch} />;
};

https://github.com/user-attachments/assets/3b0aab43-42ab-449b-baec-3815562b5d3d

Even if clearCache is called, the prefetch promise is resolved almost instantly.

I tested with a large 10MB image URL, so it's not feasible for the prefetch to complete in under 5ms.

The prefetch process should wait for all network requests to complete, and if the request has already been made once, it should resolve immediately with Promise.resolve.

While the exact timing may vary, the performance should resemble the following:

const result = await TurboImage.prefetch([{
   uri: "image",
}]); // took 500ms.

const result2 = await TurboImage.prefetch([{
   uri: "image",
}]); // Immediately returns true. took 1ms.
duguyihou commented 2 months ago

@gronxb I see. It should check if all the requests are completed. but the current implementation is to resolve immediately. I I will take a look.

duguyihou commented 2 months ago

@gronxb

some progress on iOS.

https://github.com/user-attachments/assets/a778a358-86ab-44cd-8900-1ac3c8e3838b

gronxb commented 2 months ago

Looks good to me 🚀🚀🚀

duguyihou commented 2 months ago

@gronxb

I need think about the logic in Android. I thought it should be simple. But it does not wait the requests are completed.

    imageLoader = Coil.imageLoader(context).newBuilder()
      .respectCacheHeaders(cachePolicy == "urlCache")
      .build()
    val isCompleted = imageRequests.map {
      imageLoader?.enqueue(it)?.isDisposed
    }.all { it == true }

    println("🐵 ----isCompleted $isCompleted ")
    promise.resolve("Success")
image
duguyihou commented 2 months ago

@gronxb android

https://github.com/user-attachments/assets/0949f97f-b7fe-447d-906d-6d028b04ec94

duguyihou commented 2 months ago

@gronxb

I will include this feature in the next release version. Let me know if you have any issues. Thanks.