调用后无法清除AbortController.abort()

问题描述 投票:0回答:1

各位下午好,当前,这是一个使我无法自拔的问题,我似乎看不到树木茂盛的树木。

我有一个带有内置文件上传器的React Native应用,该上传器的一部分是使用abortcontroller,它允许代码向提取请求发送信号以停止进行中的调用可以像预期的那样完美地工作,如果用户随后选择了另一个文件或尝试上载该文件,则先前取消的我的诺言立即返回,并且中止错误仍然存​​在,阻止了任何进一步的上载,为了爱也没有钱,我似乎可以找到阻止这种情况发生的方法。

这里是我的屏幕的简化版本(删除了URL,删除了一些数据点,等等),以保护我的系统的隐私

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',
  },
});

请原谅我的代码的性质,因为我仍然对本机反应还很陌生,并且只做了几个月,所以我仍然关注很多功能和系统,只是将其更新为最新版本昨天的博览会(36),所以我可以启用访存中止。

但是任何人都有任何线索,为什么在信号被调用一次以中止每个将来的请求之后,然后似乎正在获得相同的信号,而不管用户没有再次单击它并且我没有将其存储在状态中因此我似乎无法理解为什么它会持续到我甚至在开始和结束时都重新构建承诺以确保每次上传上传文件时都将其清理干净的程度。

react-native fetch expo jsx abort
1个回答
0
投票

您共享了太多代码,但是我会尝试一下。

首先,我很惊讶它甚至一开始就起作用。您正在像这样创建中止控制器:

const controller = new AbortController;

虽然应该是:

const controller = new AbortController();

现在,当涉及到逻辑时,我相信它正在显示错误,因为您使用的是已取消的相同控制器。

诀窍是将其变成一种状态,并在中止后立即将其更新为新的AbortController(可能是在调用AbortUpload之后立即在您的controller.abort()方法中。)>

© www.soinside.com 2019 - 2024. All rights reserved.