2017-09-01 77 views
2

更新:完全解決方案在答案結束。如何使api調用一個而不是併發


有了這樣的代碼:

@Injectable() 
export class FileUploader { 

    constructor(private http: Http) {} 

    upload(url: string, file: File) { 
     let fileReader: FileReader = new FileReader(); 

     return new Promise((resolve, reject) => { 
      fileReader.onloadend = (e) => { 
       // you can perform an action with read data here 
       let content = fileReader.result; 
       console.log('starting upload'); 
       return this.http.post(url, content) 
        .map((res) => res.json()).toPromise(); 
      }; 

      fileReader.readAsArrayBuffer(file); 
     }); 
    } 

和使用像

this.fileuploader.upload('/backend/upload', content).then(); // do something 

但是,當用戶選擇多個文件(就像FB創作專輯),所有的文件將在上傳同時完全阻止瀏覽器。

我的計劃是在一些屬性中放置promise數組,另一個私有方法會觸發第一個屬性;完成後,承諾會再次調用該方法,以便新的上傳開始,直到全部完成。

我試過的所有組合都失敗了,我甚至沒有編譯它們。即使上面的代碼不是我的,但從其他問題中挑選出來。

如何做到這一點?


編輯:基於@toskv答案,這是我現在使用的解決方案。我更新了答案,以防其他人遇到同樣的問題。

再次感謝@toskv的幫助。

@Injectable() 
export class FileUploader { 

    private currentTask: Promise<any> = null; 

    constructor(private http: Http) {} 

    upload(url: string, file: File) { 
     let action =() => { 
      return new Promise((resolve) => { 
       let fileReader: FileReader = new FileReader(); 
       fileReader.onloadend = (e) => { 
        let content = fileReader.result; 
        return this.http.post(url, content) 
         .map((res) => res.json()).toPromise() 
         .then((json) => { 
          resolve(json); 
         }); 
       }; 

       fileReader.readAsArrayBuffer(file); 
      }) 
     }; 

     return this.doNext(action) 
    } 

    private doNext(action:() => Promise<any>): Promise<any> { 
     if (this.currentTask) { 
      // if something is in progress do it after it is done 
      this.currentTask = this.currentTask.then(action); 
     } else { 
      // if this is the only action do it now 
      this.currentTask = action(); 
     } 
     return this.currentTask; 
    } 
} 
+0

我想'this.http'是角HTTP服務,它是利用觀測量。如果是這樣,你可以使用'mergeMap'或'concatMap'來實現所需的行爲:https://www.learnrxjs.io/operators/transformation/concatmap.html –

回答

3

如果你想在一個函數中包裝一個調用,你可以很容易地把它們包裝起來。

function chain(calls: Array<() => Promise<any>>): Promise<any> { 
    return calls.reduce((previous, current) => { 
     return previous.then(current); 
    }, Promise.resolve("")); 
} 

什麼這個函數的作用是走線槽的承諾(它與Promise.resolve創建一個已經解決的一個開始)的整個列表和承諾的列表中添加各項功能爲接收然後回調所產生的承諾。

它這樣做相當於:

Promise 
    .resolve("") 
    .then(first) 
    .then(second); 

如何使用這將是一個例子。

let first =() => { 
    return new Promise((resolve, reject) => { 
    setTimeout(() => { 
     console.log('first'); 
     resolve(); 
    }, 3000); 
    }) 
} 

let second =() => { 
    return new Promise((resolve, reject) => { 
    setTimeout(() => { 
     console.log('second'); 
     resolve(); 
    }, 100); 
    }) 
} 

chain([first, second]); 

你可以找到一個工作示例here

你也可以很容易地從這樣的項目數組中創建承諾返回函數的列表。

let files = ['a', 'b', 'c']; 

function uploadFile(file) { 
    return Promise.resolve<any>('v'); 
} 
let uploads = files.map((file) =>() => uploadFile(file)) 
chain(uploads); 

繼我們在聊天討論。它看起來真的需要的是一個可以對傳入請求進行排隊的服務。 以下是普通打字稿中的一個例子。您應該只能使用@Inject對其進行註釋,並在文件上傳服務中使用它。

class QueuedDispacherService { 
    private currentTask: Promise<any> = null; 

    constructor() { } 

    doNext(action:() => Promise<any>) { 
    if (this.currentTask) { 
     //if something is in progress do it after it is done 
     this.currentTask = this.currentTask.then(action); 
    } else { 
     if this is the only action do it now 
     this.currentTask = action(); 
    } 
    return this.currentTask; 
    } 
} 


let dispatcher = new QueuedDispacherService(); 


let action1 =() => { 
    return new Promise((resolve) => { 
    setTimeout(() => { 
     console.log('first task done!'); 
     resolve('done 1!'); 
    }, 10000); 
    }) 
} 


let action2 =() => { 
    return new Promise((resolve) => { 
    setTimeout(() => { 
     console.log('second task done!'); 
     resolve('done 2!'); 
    }, 300); 
    }) 
} 

dispatcher.doNext(action1); 
dispatcher.doNext(action2); 

的操作功能,可以創建與前一示例所示。

here它正在工作。

有了這個,你也可以通過訂閱doNext方法返回的承諾來收聽每個上傳。

dispatcher.doNext(action1).then((result) => console.log(result)); 
dispatcher.doNext(action2).then((result) => console.log(result)); 

的代碼可以,如果你初始化currentTask有解決的承諾縮短。

class QueuedDispacherService { 
    private currentTask: Promise<any> = Promise.resolve(); 

    constructor() { } 

    doNext(action:() => Promise<any>) { 
    return this.currentTask = this.currentTask.then(action); 
    } 
} 

此圖顯示了在運行時會發生什麼。最終Promise API所做的一切就是幫助您更好地處理回調。

enter image description here

+0

事情是,我不能發送數組,但只能逐個。而且我在ts和ng中是完整的初學者,但我會從你的回答開始並從那裏開始工作。 我正在將您的答案標記爲已接受,但如果對您不難,請您從我的FileUploader服務器上寫出正確的解決方案嗎?我仍然失去了承諾和觀察。 – Zeljko

+0

你怎麼稱呼這個方法?你如何接收不能作爲數組發送的文件?對此提出問題。 ;) – toskv

+0

這個函數是從應用程序中的多個組件調用的。用戶將從組件A中選擇文件併發送文件數組,這不是問題。但是,當這是上傳時,用戶將從組件B中選擇另一個文件。 還有另一個原因;我打算在上傳之前進行圖像大小調整。這就是爲什麼我需要一個接一個的承諾,所以當用戶選擇20多個文件時,我不會殺死瀏覽器。 – Zeljko

相關問題