更新:答案结束时的完整解决方案。
使用这样的代码:
@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 :(得分: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都是为了帮助您更好地处理回调。