Невозможно очистить AbortController.abort () после вызова - PullRequest
0 голосов
/ 18 февраля 2020

Добрый день, ребята, вот проблема, которая в настоящее время ведет меня к стене, и я не могу видеть дерево для деревьев.

У меня есть приложение React Native со встроенным средством загрузки файлов, часть этот загрузчик - это использование abortcontroller, который позволяет коду отправить сигнал в запрос на выборку, чтобы остановить вызов в полете. Это работает отлично, так как можно было бы ожидать, что проблема заключается в том, что пользователь затем выбирает другой файл или пытается загрузить файл ранее отмененное мое обещание возвращается немедленно с ошибкой отмены, которая все еще остается на месте, предотвращая дальнейшие загрузки, и ради любви и денег я не могу найти способ предотвратить это.

Вот уменьшенная версия моего экрана (URL-адреса удалены, некоторые точки данных удалены, и т. д. c), чтобы защитить конфиденциальность моей системы

import React from 'react';
import {StyleSheet,View,ScrollView,Alert,} from 'react-native';
import AppSettings from '../../constants/AppSettings'
import Colours from '../../constants/Colours';
import CustomHeader from '../../components/CustomHeader';
import CustomButton from '../../components/CustomButton';
import TextContent from '../../components/TextContent';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
import Constants from 'expo-constants';
import * as DocumentPicker from 'expo-document-picker';
import * as Permissions from 'expo-permissions';
import CustomInput from '../../components/CustomInput';
import Progressbar from '../../components/ProgressBar';
import * as Network from 'expo-network';

export default class UploadScreen extends React.Component {

  state = {
    file: null,
    filetype: null,
    fileExtention:null,
    uploading: false,
    pickResult: null,
    mainDataLoaded: false,
    docDesc:null,
    enteredPassword:'',
    currentUploadPercent:0,
    uploadTime: 0,
    startPick: false,
  };
render() {


    return (
      <View style={styles.screenContainer}>
      <LinearGradient start={[0, 1]} end={[0,0.9]} colors={['rgba(163, 163, 163,1)', 'rgba(163, 163, 163,0)']} style={{flex:1}} >
        <ScrollView style={styles.scrollContainer} contentContainerStyle={styles.defaultContainer}>
          <View style={{flex:1, alignItems: 'center', justifyContent: 'center', width:'100%' }}>
            <TextContent>Upload file containing: {this.state.docDesc}</TextContent>
            {this._maybeRenderFile()}
            {this._maybeRenderControls()}
            {this._maybeRenderUploadingIndicator()}
          </View>
        </ScrollView>
      </LinearGradient>
      </View>
    );
  }

  _maybeRenderUploadingIndicator = () => {

      if (this.state.uploading) {
      return (
        <View style={{width:'80%',alignItems:'center'}}>
          <Progressbar progress={this.state.currentUploadPercent}/>
          <CustomButton style={{width:'100%'}} onPress={()=>{AbortUpload(this)}} title='Cancel Upload'></CustomButton>          
        </View>


      );
    }
  };
  _maybeRenderControls = () => {
    if (!this.state.uploading) {
      return (
        <View style={{width:'100%',alignItems:'center'}}>
          <CustomButton style={{width:'80%'}} onPress={this._pickImage} title='Select file to upload'><MaterialCommunityIcons style={{color:Colours.PrimaryButtonText}} name="folder-open" size={30}/></CustomButton>
        </View>
      );
    }
  };
  _maybeRenderFile = () => {
    if (this.state.file) {
      switch (this.state.filetype) {
        case 'application/pdf':
          const passwordHandler = enteredText => {
            this.setState({enteredPassword: enteredText});
          };
          return (
            <View style={{alignItems:'center'}}>
              <MaterialCommunityIcons style={{color:Colours.PrimaryText}} name="file-pdf" size={100}/>
              <TextContent style={{textAlign:'center'}}>File to upload: {this.state.file}</TextContent>
              <TextContent>If this file requires a password to access please type it below or leave blank if not required.</TextContent>
              {!this.state.uploading && (
                <View>
                  <CustomInput placeholder='PDF Password (if applicable)' autoCapitalize='characters' autoCompleteType='off' autoCorrect={false} textContentType='none' onChangeText={passwordHandler} value={this.state.enteredPassword}/>
                  <CustomButton style={{width:'100%'}} onPress={()=>{this._handleImagePicked(this.state.pickResult)}} title='Upload this file'><MaterialCommunityIcons style={{color:Colours.PrimaryButtonText}} name="file-upload-outline" size={30}/></CustomButton>
                  <TextContent style={{textAlign:'center'}}>Or</TextContent>
                </View>
              )}

            </View>
          );    
          break;
        case 'image/jpg':
        case 'image/png':
        case 'image/gif':
          return (
            <View style={{alignItems:'center'}}>
              <MaterialCommunityIcons style={{color:Colours.PrimaryText}} name="file-image" size={100}/>
              <TextContent style={{textAlign:'center'}}>File to upload: {this.state.file}</TextContent>
              {!this.state.uploading && (
                <View>
                  <CustomButton style={{minWidth:'80%'}} onPress={()=>{this._handleImagePicked(this.state.pickResult)}} title='Upload this file'><MaterialCommunityIcons style={{color:Colours.PrimaryButtonText}} name="file-upload-outline" size={30}/></CustomButton>
                  <TextContent style={{textAlign:'center'}}>Or</TextContent>
                </View>
              )}
            </View>
          );
          break;
        default:
          break;
      }

    }
  };

  _askPermission = async (type, failureMessage) => {
    const { status, permissions } = await Permissions.askAsync(type);

    if (status === 'denied') {
      alert(failureMessage);
    }
  };


  _pickImage = async () => {
    await this._askPermission(
      Permissions.CAMERA_ROLL,
      'We need the file permission to access files from your phone...'
    );
    if(!this.state.startPick){
      this.setState({startPick: true})
      let pickerResult = await DocumentPicker.getDocumentAsync({});

      if(pickerResult.type == 'success'){
        this.setState({startPick: false})
        //Get file extention
        var splitAt = pickerResult.name.lastIndexOf(".")
        var fileExt = pickerResult.name.slice(splitAt,pickerResult.name.length).toLowerCase()
        switch (fileExt) {
          case '.pdf':
            this.setState({file: pickerResult.name, filetype: 'application/pdf', pickResult: pickerResult, fileExtention: fileExt})
            break;
          case '.jpg':
            this.setState({file: pickerResult.name, filetype: 'image/jpg', pickResult: pickerResult, fileExtention: fileExt})
            break;
          case '.jpeg':
            this.setState({file: pickerResult.name, filetype: 'image/jpg', pickResult: pickerResult, fileExtention: fileExt})
            break;
          case '.png':
            this.setState({file: pickerResult.name, filetype: 'image/png', pickResult: pickerResult, fileExtention: fileExt})
            break;
          case '.gif':
            this.setState({file: pickerResult.name, filetype: 'image/gif', pickResult: pickerResult, fileExtention: fileExt})
            break;
          default:
            this.setState({file: null, filetype: null, pickResult: null})
            Alert.alert('Unsupported filetype','For security reasons you may only select images or PDF files to upload.')
            break;
        }
      }else{
        //No file selected
        this.setState({file: null, filetype: null, pickResult: null})
        this.setState({startPick: false})
      }
      if(__DEV__){console.log('Result:', pickerResult)}
    }else{
      if(__DEV__){console.log('Pick already started')}
    }

  };
_StatusCheck = async() =>{
  return fetch('Url for server side upload status response')
  .then((response) => response.json())
  .then((responseJson) => {
    return responseJson
  })
  .catch((error) =>{
    console.error(error);
  });
}


  _handleImagePicked = async pickerResult => {
    try {

      if (!pickerResult.cancelled) {
        var thisTime = Date.now()
        this.setState({uploadTime: thisTime})
        var myPromise = new Promise(function(){})
        myPromise = MakeQuerablePromise(new uploadFileAsync(
          pickerResult.uri,
          this.state.docType + '-' + Date.now() + this.state.fileExtention,
          this.state.filetype,
          this.state.docType,
          this.state.docDesc,
          this.state.enteredPassword,
          this.state.quoteID,
          this.state.uploading,
          thisTime,
          controller.signal
        ));
        this.setState({ uploading: true });
        if(__DEV__){
          console.log("Initial fulfilled:", myPromise.isFulfilled());//false
          console.log("Initial rejected:", myPromise.isRejected());//false
          console.log("Initial pending:", myPromise.isPending());//true
        }
        for (let index = 0; index < 1;) {

          var currentStatus = await this._StatusCheck()
          var curTime = new Date()
          if(__DEV__){
            console.log('Time:',curTime.getHours(),':',curTime.getMinutes(),':',curTime.getSeconds())
            console.log('Status:',currentStatus)
            console.log("Promise status- fulfilled:", myPromise.isFulfilled(), "rejected:", myPromise.isRejected(),"pending:", myPromise.isPending());//false
            console.log('Promise Content:',myPromise)
          }
          if(!myPromise.isRejected()){
            if(currentStatus.percent != undefined){
              if(currentStatus.percent < 100){
                this.setState({currentUploadPercent:currentStatus.percent})
                if(__DEV__){
                console.log('Upload progess ' + currentStatus.percent)
                console.log("Promise status: fulfilled:", myPromise.isFulfilled(), "rejected:", myPromise.isRejected(),"pending:", myPromise.isPending());//false
                }
              }else{
                this.setState({currentUploadPercent:currentStatus.percent})
                if(__DEV__){
                console.log('Upload progess 100%')
                console.log("Promise status: fulfilled:", myPromise.isFulfilled(), "rejected:", myPromise.isRejected(),"pending:", myPromise.isPending());//false
                }
              }
            }
          }

          if(myPromise.isFulfilled() == true){
            if(__DEV__){
            console.log("Entered Fulfilled State - Promise status: fulfilled:", myPromise.isFulfilled(), "rejected:", myPromise.isRejected(),"pending:", myPromise.isPending());//false
            }
            index++
          }
          if(myPromise.isRejected() == true){
            if(__DEV__){
            console.log("Entered Rejected State - Promise status: fulfilled:", myPromise.isFulfilled(), "rejected:", myPromise.isRejected(),"pending:", myPromise.isPending());//false
            }
            index++
          }

        }
        if(myPromise.isRejected() == false){
          myPromise.then(response => response.json()).then((responseJson)=>{
            if(__DEV__){
            console.log('Promise Json:',responseJson)
            console.log("Final fulfilled:", myPromise.isFulfilled());//true
            console.log("Final rejected:", myPromise.isRejected());//false
            console.log("Final pending:", myPromise.isPending());//false
          }
            if(responseJson.datapage.result.gitUploadStatus.successful == true){
              //Successful upload
              this.props.navigation.navigate('CaptureThanks')
            }else{
              //Upload had a issue
              Alert.alert('Upload error','There was an issue with the upload, this maybe a temporary issue with your connection or with the server please ensure you have a good steady signal and try again, if the problem persists please contact us. Error Code: '+responseJson.datapage.gitUploadStatus.errorMessage)
            }
          })
        }else{
          //Rejected promise handle failure
          if(myPromise.rejectReason() == 'AbortError'){
            myPromise = MakeQuerablePromise(new Promise(function(resolve, reject){
              resolve('AbortError')
            }))
          }else{
            Alert.alert('Upload error','There was an issue with the upload, this maybe a temporary issue with your connection or with the server please ensure you have a good steady signal and try again, if the problem persists please contact us. Error Code: '+responseJson.datapage.gitUploadStatus.errorMessage)
          }
        }
      }
    } catch (e) {
      if(__DEV__){
        console.log('Error Name:',e.name)
        console.log('Catch Error:',{ e });
      }
        return;
    } finally {
      if(__DEV__){
        console.log('Reached Final')
      }
      myPromise = MakeQuerablePromise(new Promise(function(resolve, reject){
        resolve('Finished')
      }))
      console.log(myPromise)
      this.setState({ uploading: false, currentUploadPercent:0 });
    }
  };
}
function MakeQuerablePromise(promise) {
  // Don't modify any promise that has been already modified.
  if (promise.isResolved){
    return promise
  };
  // Set initial state
  var isPending = true;
  var isRejected = false;
  var isFulfilled = false;
  var rejectReason = '';

  // Observe the promise, saving the fulfillment in a closure scope.
  var result = promise.then(
      function(v) {
          isFulfilled = true;
          isPending = false;
          rejectReason = '';
          return v; 
      }, 
      function(e) {
          isRejected = true;
          isPending = false;
          rejectReason = e.name;
          return e; 
      }
  );
  result.isFulfilled = function() { return isFulfilled; };
  result.isPending = function() { return isPending; };
  result.isRejected = function() { return isRejected; };
  result.rejectReason = function() {return rejectReason; };
  return result;
}
const controller = new AbortController;
async function uploadFileAsync(uri,name,type,docType,docDesc,password,quoteid,isUploading,uploadTime,abortSignal) {
  if(!isUploading){

    if(__DEV__){console.log('Making upload request for ',docType,' Document description:', docDesc)}
    let apiUrl = 'Url to push the upload to';

    let formData = new FormData();
    //(method) FormData.append(name: string, value: string | Blob, fileName?: string): void
    formData.append('filename', {
      uri,
      name: name,
      type: type,
      documentType:docType,
      description:docDesc,
      password:password,
      quoteid:quoteid,
    });
    let options = {
      method: 'POST',
      body: formData,
      headers: {
        Accept: 'application/json',
        'Content-Type': 'multipart/form-data',
      },
      signal: abortSignal,

    };
    if(__DEV__){console.log('Options:', options)}
    return fetch(apiUrl, options);
  }else{
    return null
  }
}
async function AbortUpload(stateObject){
  controller.abort()
  stateObject.setState({isUploading: false})
}

UploadScreen.navigationOptions = {
  header: () => <CustomHeader goback={true} title='Document Upload'/>,
  title: AppSettings.AppName + ' ',
  headerTitleStyle:{
    fontFamily: AppSettings.HeaderFont,
  },
  headerStyle: {
      backgroundColor: Colours.HeaderBackground
    },
    headerTintColor: Colours.HeaderText
};
const styles = StyleSheet.create({
  screenContainer:{
      flex:1,
      backgroundColor: Colours.PrimaryBackgroud,
  },
  scrollContainer:{
      flex: 1,
      height:'100%'
  },
  defaultContainer: {
    alignItems: 'center',
  },
});

Извините за характер моего кода, так как я все еще довольно нов, чтобы реагировать на нативный и только делал это в течение нескольких месяцев, так что до сих пор в моей голове много функций и сист и только недавно обновил до последней версии expo (36) вчера, чтобы я мог включить прерывание извлечения.

Но у кого-то есть какие-либо подсказки относительно того, почему кажется, что после того, как сигнал вызывается один раз, чтобы прервать каждый тогда в будущем запросе, похоже, будет получен тот же сигнал, несмотря на то, что пользователь не щелкнул его снова, и я не храню его в состоянии, поэтому я не могу понять, почему он сохраняется до такой степени, что я даже дошел до крайности восстановления обещания в начале и в конце, чтобы гарантировать, что они были очищены при каждой загрузке.

1 Ответ

1 голос
/ 19 февраля 2020

Вы поделились слишком большим количеством кода, но я попробую это сделать.

Прежде всего, я удивлен, что это вообще сработало. Вы создаете свой контроллер прерывания следующим образом:

const controller = new AbortController;

Хотя это должно быть:

const controller = new AbortController();

Теперь, когда дело доходит до логики c, я считаю, что он отображает ошибка, потому что вы используете тот же контроллер, который уже был отменен.

Хитрость заключается в том, чтобы перевести его в состояние и обновить его новым AbortController, как только он будет прерван (возможно, в вашем AbortUpload метод, сразу после вызова controller.abort()).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...