如何逐个调用api而不是并发调用

时间:2017-09-01 12:56:19

标签: angular typescript

更新:答案结束时的完整解决方案。

使用这样的代码:

@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;
    }
}

1 个答案:

答案 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都是为了帮助您更好地处理回调。

enter image description here