【问题标题】:How to use js async/await in mutiple async requests如何在多个异步请求中使用 js async/await
【发布时间】:2020-03-01 12:21:13
【问题描述】:

我正在使用Angular 7,现在有一个方法(Angular guard CanActivate)包含一些嵌套的http调用方法,我需要在所有嵌套的http调用完成后返回数据。

如下代码所示,只有在getCurrentUser()完成后,才会在canActivate()中返回结果,而现在,因为getCurrentUser()还没有完成,所以总是返回false。

export class AuthGuard implements  CanActivate{


  constructor(private commonService: CommonService) {
  }

  async canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
    console.log('======');
    await this.getCurrentUser();
    return this.hasAccess;
  }

  hasAccess: boolean = false;

  async getCurrentUser(){
    await this.commonService.getToken().subscribe(token => {
      this.commonService.getCurrentUser(param, token).subscribe(o => {
        if(o.success){

          this.hasAccess = true;

        }else {
            window.location.href = '/forbidden.html';
          }
      }, error => {
        console.log(error);
      });
    });
    console.log("async");
  }
}

你可以看到有两个异步方法A,B应该是await,A,B不是并行的,我查看了有关Promise和async/await的文档,没有找到解决方案。

由于 await 应该始终遵循 async,我该如何让 canActivate() 在所有异步 http 调用完成后返回结果?

+++更新

this.commonService.getToken()this.commonService.getCurrentUser(param, token)是http调用(HttpClient),我尝试了很多解决方案,但没有结果。

【问题讨论】:

    标签: javascript asynchronous async-await es6-promise async.js


    【解决方案1】:

    Promise.all() 方法就是你要找的。​​p>

    【讨论】:

    • 嗨hugojavadi,你能分享一下Promise.all()的详细信息吗,我是新手。谢谢!
    • 非常感谢您的帮助,我检查了您发布的文档,发现无论有多少异步请求,它仍然是两层。就像文档一样,方法 A Promise.all([1, 2, 3]),而在我的情况下,1 和 2 两层请求它可能需要 async 2 await 3。
    【解决方案2】:

    您可以使用async awaitPromise.all 的组合。通过这种方式,您可以等待所有异步网络请求,并在所有请求完成后执行一些操作。

    Promise.all() 接受一组 Promise 并将它们包装成一个 Promise。而且我们已经知道一些处理单个 Promise 的好语法。我们可以等待它。

    为了您的理解,请查看此代码示例:

    let films = await Promise.all(
      characterResponseJson.films.map(async filmUrl => {
        let filmResponse = await fetch(filmUrl)
        return filmResponse.json()
      })
    )
    console.log(films)
    

    我从这篇文章中引用了这个例子,它可以帮助你找出你的解决方案

    How to use async/await with map and Promise.all

    更新: 对于您的用例,您可以这样使用:

    async getCurrentUser(){
      await this.commonService.getToken().subscribe(async token => {
        await this.commonService.getCurrentUser(param, token).subscribe(o => {
          if(o.success){
    
            this.hasAccess = true;
    
          }else {
              window.location.href = '/forbidden.html';
            }
        }, error => {
          console.log(error);
        });
      });
      console.log("async");
    }
    

    【讨论】:

    • 感谢您的回答,而对于我的情况,我如何 Promise.all() 进行嵌套异步调用 ts await this.commonService.getToken().subscribe(token =&gt; { this.commonService.getCurrentUser(param, token).subscribe(o =&gt; {
    • 也使用 try catch 块来处理错误。上面的代码将无法处理错误@Ankit
    • @KDFinal 我已经根据您的特定需求更新了答案。这会有所帮助
    • @Ankit canActivate()this.commonService.getCurrentUser()完成之前返回结果,它不等待这个异步请求,你知道吗?
    • @KDFinal 你把它包裹在 Promise.all() 里面了吗
    【解决方案3】:

    asyncawait 建立在 Promise 之上。 Promise 是 javascript 中的一个特殊对象,广泛用于避免 callback 地狱

    在使用 asyncawait 时,try catch 块也很重要,因为我们还需要处理错误,以防 API 失败。

    hasAccess: boolean;
    canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise < boolean > {
        this.getCurrentUser();
        return this.hasAccess;
    }
    
    async getCurrentUser() {
        try {
            const output1 = await Promise.resolve(fetch(urlOfToken).then(res => res.json())) // if GET call, if POST you can postData('', {})
            const currentUser = await this.postData(
                `URL fetching current user`,
                {
                    token: `access token from object ${output} `,
                    param: 'any other param'
                }
            );
            // Check in currentUser Object whether response contains user or not
            // If user exists set this.hasAccess = true;
            // IF not set this.hasAccess = false;
        } catch (error) {
            // Error Handling
            console.log(error);
        }
    }
    // Courtesy MDN
    async postData(url = '', data = {}) {
        // Default options are marked with *
        const response = await fetch(url, {
            method: 'POST', // *GET, POST, PUT, DELETE, etc.
            mode: 'cors', // no-cors, *cors, same-origin
            cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
            credentials: 'same-origin', // include, *same-origin, omit
            headers: {
                'Content-Type': 'application/json'
                // 'Content-Type': 'application/x-www-form-urlencoded',
            },
            redirect: 'follow', // manual, *follow, error
            referrer: 'no-referrer', // no-referrer, *client
            body: JSON.stringify(data) // body data type must match "Content-Type" header
        });
        return await response.json(); // parses JSON response into native JavaScript objects
    }
    

    关于如何使用promises 以及asyncawait 的额外参考。还介绍了如何进行并行、序列和竞赛 API 调用

    const urls = [
        'https://jsonplaceholder.typicode.com/users',
        'https://jsonplaceholder.typicode.com/albums',
        'https://jsonplaceholder.typicode.com/posts'
    ];
    
    // BASIC
    
    Promise
        .all(urls.map(url => {
            return fetch(url).then(res => res.json())
        }))
        .then((results) => {
            console.log(results[0]);
            console.log(results[1]);
            console.log(results[2]);
        })
        .catch(() => console.log('error!'));
    
    
    // async await
    // built atop of promises
    // benefit is it is easier to make code read easier nothing more promises can get job done actually
    const getData = async function () {
        try {
            const [users, albums, posts] = await Promise.all(urls.map(url => {
                return fetch(url).then(res => res.json())
            }));
            console.log('users', users);
            console.log('albums', albums);
            console.log('posts', posts);
        } catch (error) {
            console.log('Oops!');
        }
    }
    
    // for await of
    const getData2 = async function () {
        const arrayOfPromises = await urls.map(url => fetch(url));
        for await (let request of arrayOfPromises) {
            const response = await request.json();
            console.log(response);
        }
    }
    
    
    const a = () => promisify('a', 100); // I am making it asynchronous here ; Can be API call or any asynchronus task
    const b = () => promisify('b', 1000);// I am making it asynchronous here ; Can be API call or any asynchronus task
    const c = () => promisify('c', 5000);// I am making it asynchronous here ; Can be API call or any asynchronus task
    
    const promisify = (item, delay) =>
        new Promise((resolve) =>
            setTimeout(() =>
                resolve(item), delay));
    
    // Parallel
    
    async function parallel() {
        const [output1, output2, output3] = await Promise.all([a(), b(), c()]);
        return `parallel done right: ${output1} ,  ${output2} , ${output3}`;
    }
    
    // race
    
    async function race() {
        const output1 = await Promise.race([a(), b(), c()]);
        return `race done right: ${output1}`;
    }
    
    // sequence
    
    async function sequence() {
        const output1 = await a();
        const output2 = await b();
        const output3 = await c();
        return `sequenece done right: ${output1}, ${output2}, ${output3}`;
    }
    
    
    parallel().then(console.log);
    race().then(console.log);
    sequence().then(console.log);
    

    【讨论】:

      【解决方案4】:

      参考上面的答案和其他人的帮助,我更新了我的代码,现在它可以工作了。 我的更新是在 getToken() 中使用 new Promise()getUser() 而不是 await 它,Promise 有状态(待定 ,resolved,rejected),一旦状态改变就不会再改变,这样一来,一旦Promise的状态改变为resloved em>,它不会被改变,Promise 将返回它的值,否则如果更改为 reject 则会出错。

      附上我更新的代码如下:

      可以激活

      async canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
          console.log('======');
          let token = await this.getToken();
          // let hasAccess = await this.getUser(token);
          return await this.getUser(token);
        }
      
      

      getToken()getUser()

      // return a Promise object and resolve(token)
      getToken(){
          return new Promise((resolve, reject)=>{
            this.commonService.getToken().subscribe(token=>{
              resolve(token)
            })
          })
        }
      
        getUser(token: any) {
          return new Promise<boolean>((resolve, reject) => {
            this.commonService.getCurrentUser(param, token).subscribe(o => {
              if(o.success){
      
                hasAccess = true;
              }else {
                window.location.href = '/forbidden.html';
              }
              resolve(hasAccess);
            }, error => {
              console.log(error);
              resolve(hasAccess);
            });
          })
        }
      

      我对async/awaitPromise不是很熟悉,欢迎指正错误。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-10-26
        • 2018-10-27
        • 2019-01-17
        • 1970-01-01
        • 2015-03-16
        • 2016-09-03
        • 2021-03-16
        相关资源
        最近更新 更多