2017-09-01 41 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

相关问题