最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How can I use Promises in Aurelia? - Stack Overflow

programmeradmin1浏览0评论

I have 2 files 1. api_service.ts

import { HttpClient } from 'aurelia-http-client';
import { autoinject } from "aurelia-framework";
import { ObserverLocator } from "aurelia-binding";

@autoinject()
export class ApiServices {
    private app_url = '/';
    private host = ':8080';

    constructor(private store: Store, private client: HttpClient, private observerLocator: ObserverLocator) { }

    GetInstallationId() {
        let url = this.host + "/1/api/iid";
        let iid;
        this.client.get(url)
            .catch((err) => { })
            .then(res => {
                let response = JSON.parse(res.response);
                iid = response.iid;
            });
        return iid;
    }
}

here I am trying to get some value from the server and in the HTTP response, I can see that I am getting it.

  1. board_services.ts

import { Store } from './localstorage_service';
import { ApiServices } from './api_service';
import { autoinject } from "aurelia-framework";
import { ObserverLocator } from "aurelia-binding";

@autoinject()
export class AppServices {
	private installation_ID: string;

	constructor(private store: Store, private apiServices: ApiServices, private observerLocator: ObserverLocator) { }

	getInstallationID() {
		const iid = this.store.getValue('installation_ID');
		console.log("iid = " + iid);
		if (iid && iid != undefined) {
			console.log('inside if iid');
			// this.installation_ID = this.apiServices.GetInstallationId();
			this.installation_ID = iid;
		} else {
			console.log('inside else iid');
			this.installation_ID = this.apiServices.GetInstallationId();
		}
		this.store.setValue('installation_ID', this.installation_ID);
		console.log("after if condition iid = " + iid);
		return this.installation_ID;
	}
}

I have 2 files 1. api_service.ts

import { HttpClient } from 'aurelia-http-client';
import { autoinject } from "aurelia-framework";
import { ObserverLocator } from "aurelia-binding";

@autoinject()
export class ApiServices {
    private app_url = 'http://example./';
    private host = 'http://api.example.:8080';

    constructor(private store: Store, private client: HttpClient, private observerLocator: ObserverLocator) { }

    GetInstallationId() {
        let url = this.host + "/1/api/iid";
        let iid;
        this.client.get(url)
            .catch((err) => { })
            .then(res => {
                let response = JSON.parse(res.response);
                iid = response.iid;
            });
        return iid;
    }
}

here I am trying to get some value from the server and in the HTTP response, I can see that I am getting it.

  1. board_services.ts

import { Store } from './localstorage_service';
import { ApiServices } from './api_service';
import { autoinject } from "aurelia-framework";
import { ObserverLocator } from "aurelia-binding";

@autoinject()
export class AppServices {
	private installation_ID: string;

	constructor(private store: Store, private apiServices: ApiServices, private observerLocator: ObserverLocator) { }

	getInstallationID() {
		const iid = this.store.getValue('installation_ID');
		console.log("iid = " + iid);
		if (iid && iid != undefined) {
			console.log('inside if iid');
			// this.installation_ID = this.apiServices.GetInstallationId();
			this.installation_ID = iid;
		} else {
			console.log('inside else iid');
			this.installation_ID = this.apiServices.GetInstallationId();
		}
		this.store.setValue('installation_ID', this.installation_ID);
		console.log("after if condition iid = " + iid);
		return this.installation_ID;
	}
}

here in this code I'm calling the function GetInstallationId() from api_services.ts but it is not waiting for the http request to plete. Is there a way to make the statement in the next statement execute after the GetInstallationId() is executed?

Share Improve this question edited Apr 5, 2017 at 6:11 Dwayne Charrington 6,6328 gold badges43 silver badges63 bronze badges asked Apr 4, 2017 at 13:35 Bimalmithran P.BBimalmithran P.B 1011 silver badge15 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 7

The short answer: you need to support asynchronous behavior.

You may do this via (a) Promises and/or (b) async/await. The former is supported in all browsers except IE 11 and there are polyfills like this one if you need to support IE 11. The latter is currently not supported in IE at all and as it requires new JS keywords/syntax must be used with a transpiler like Babel.

This answer will use Promises as they have fewer dependencies than async/await. The snippets below have been updated to use Promises -- look for the ments like:

// REPLACED THIS LINE ... // WITH THIS LINE

and

// ADDED THIS LINE

which identify the updates.

  1. GetInstallationId is an asynchronous function and must return a promise.

    
    class ApiServices { 
        ... 
        GetInstallationId() {
            let url = this.host + "/1/api/iid";
            return new Promise(function(accept, reject) {                
                let iid;
                this.client.get(url)
                    .catch((err) => { })
                    .then(res => {
                        let response = JSON.parse(res.response);
                        // REPLACED THIS LINE
                        // iid = response.iid;
                        // WITH THIS LINE which accepts the promise
                        accept(response.iid);
                    })
                    // ADDED THIS LINE to catch any errors 
                    // e.g. when invalid response received
                    .catch(reject);        
            });
        }
    
    }
    
  2. Because getInstallationID calls GetInstallationId, it too must return a promise. This example defines a separate function, handler, which is then bound via .bind(this) to ensure that whenever the function is executed, this will always refer back to the correct ApiService object.

    
    class AppServices {
        ...
        getInstallationID() {
    
    
      function handler(accept, reject) {
        const iid = this.store.getValue('installation_ID');
        console.log("iid = " + iid);
        if (iid && iid != undefined) {
            console.log('inside if iid');
            // this.installation_ID = this.apiServices.GetInstallationId();
            this.installation_ID = iid;
        } else {
            console.log('inside else iid');
            // REPLACED THIS LINE
            // this.installation_ID = this.apiServices.GetInstallationId();
            // WITH THIS LINE
            this.apiServices.GetInstallationId().then(accept,reject);
        }
        this.store.setValue('installation_ID', this.installation_ID);
        console.log("after if condition iid = " + iid);
        // REPLACED THIS LINE
        // return this.installation_ID;
        // WITH THIS LINE
        accept(this.installation_ID); 
      };
    
      return new Promise(handler.bind(this));        
    }        
    ...
    
    }

    The above function is just one example which will work cross browser. Another solution which requires ES6 support and/or transpilation is to use arrow functions:

    
        //
        // Arrow function version
        //
    class AppServices {
        ...
        getInstallationID() {
          // NOTE the => which is shorthand for `(function (accept,reject) {...}).bind(this)`
          return new Promise((accept, reject) => {
            const iid = this.store.getValue('installation_ID');
            console.log("iid = " + iid);
            if (iid && iid != undefined) {
                console.log('inside if iid');
                // this.installation_ID = this.apiServices.GetInstallationId();
                this.installation_ID = iid;
            } else {
                console.log('inside else iid');
                // REPLACED THIS LINE
                // this.installation_ID = this.apiServices.GetInstallationId();
                // WITH THIS LINE
                this.apiServices.GetInstallationId().then(accept,reject);
            }
            this.store.setValue('installation_ID', this.installation_ID);
            console.log("after if condition iid = " + iid);
            // REPLACED THIS LINE
            // return this.installation_ID;
            // WITH THIS LINE
            accept(this.installation_ID); 
          });        
        }        
        ...
    }
    

With the above refactoring, you can now call getInstallationID() from e.g. another view model like this:

import {AppServices} from '...';
import {autoinject} from 'aurelia-framework';
@autoinject()
export class SomeViewModel {
    constructor(appServices) {
        this.appServices = appServices;
    }

    callGetInstallationId() {

        this.appServices.getInstallationID().then(function(id) {
            // the ID that is passed here is the one that is `accept`ed.
            // ie. if AppServices.getInstallationID calls accept('123'), then 
            // the '123' will be passed to this function as the first argument
        }, function(e) {
            // this function will be called if AppServices.getInstallationID
            // calls reject.  e will be the Error object, if any, that is passed
            // to the reject method.
        });

    }
}
发布评论

评论列表(0)

  1. 暂无评论