javascript - Varför tror Typescript att async/await returnerar ett värde inneslutet i ett löfte?
Jag vill omstrukturera en promise-kedja till async/await, men Typescript klagar på typningen.
TS2322: Typen 'IHttpPromiseCallbackArg
' kan inte tilldelas typen 'IResp'...
Jag trodde att await skulle returnera ett vanligt värde, inte ett löfte. Har jag fel? Om så är fallet, hur kan jag tilldela en typning så att önskad kod kommer att kompilera?
Jag trodde att await skulle returnera samma värde som det första argumentet i .then callbacken. Har jag fel?
Gammal kod:
handleSubmit(form) {
const params = this.getParams(form);
this.myAsyncRequest(params)
.then((resp:IResp) => this.processResp(resp))
.catch((e:IServerError) => this.handleError(e));
}
Önskad ny kod:
async handleSubmit(form) {
const params = this.getParams(form);
try {
const resp:IResp = await this.myAsyncRequest(params); //typing error with "const resp:IResp"
this.processResp(resp);
} catch (e:IServerError) {
this.handleError(e);
}
}
Den önskade koden går fortfarande sönder om jag tar bort returtypen i myAsyncRequest ; jag antar att Typescript härleder direkt från AngularJS-biblioteket.
myAsyncRequest(params:IParams):IHttpPromise<IResp> {
return $http.post('blah', data);
}
Om jag tar bort "IResp" från const resp-deklarationen, klagar processResponse på att IHttp
processResp(resp:IResp) {
//do stuff
}
Svar #1
Din fråga "Jag trodde att await skulle returnera samma värde som det första argumentet i .then callback. Har jag fel?".
Nej, du har helt rätt. Men du har fel om vad det första argumentet i .then-callbacken är.
Du definierar myAsyncRequest för att returnera IHttpPromise<IResp>. Men IHttpPromise<T> är definierad som att den ärver från IPromise på följande sätt:
type IHttpPromise<T> = IPromise<IHttpPromiseCallbackArg<T>>;
Så, ett IHttpPromise<T> är ett löfte som returnerar ett IHttpPromiseCallbackArg<T> tillbaka där den faktiska datan av typen T finns i data-egenskapen hos IHttpPromiseCallbackArg<T>.
Så, den gamla kodvarianten vi ser i frågan:
handleSubmit(form) {
const params = this.getParams(form);
this.myAsyncRequest(params)
.then((resp:IResp) => this.processResp(resp))
.catch((e:IServerError) => this.handleError(e));
}
bör faktiskt inte kompilera utan fel i TypeScript när myAsyncRequest är definierad för att returnera IHttpPromise.
Hur man fixar detta:
async handleSubmit(form) {
const params = this.getParams(form);
try {
const httpResp:IHttpPromiseCallbackArg<IResp> = await this.myAsyncRequest(params);
const resp: IResp = httpResp.data;
this.processResp(resp);
} catch (e:IServerError) {
this.handleError(e);
}
}
Observera: I de senaste typedefinitionerna för Angular kallas typen IHttpPromiseCallbackArg<T> faktiskt IHttpResponse<T>.
Kanske i din kod har du definierat IResp som IHttpResponse<Something>? Då har du bara en konflikt med det gamla namnet IHttpPromiseCallbackArg. Hämta då de senaste typdefinitionerna från DefinitelyTyped som använder det nya namnet. Och du skulle också behöva ändra definitionen av myAsyncRequest till:
myAsyncRequest(params:IParams):IHttpPromise<Something> {
Svar #2
Raden som innehåller await väntar faktiskt på det lösda värdet - men eftersom funktionen själv är asynkron (för att tillåta allt det väntandet) får du tillbaka ett löfte.
Exempel... i koden nedan kan du använda x som ett vanligt nummer (även om delay returnerar ett löfte) och återigen för y - så allt som du väntar på är löst så att du kan använda det mer som om det vore synkront.
Den asynkrona funktionen som inte ser ut att returnera ett löfte gör det nu.
Detta kan verka förvirrande, eftersom det verkar "vända på löftena", men vad det gör är att flytta then till översta nivån (du kan ha asynkrona funktioner som anropar andra asynkrona funktioner och så vidare).
function delay(ms: number) {
return new Promise<number>(function(resolve) {
setTimeout(() => {
resolve(5);
}, ms);
});
}
async function asyncAwait() {
let x = await delay(1000);
console.log(x);
let y = await delay(1000);
console.log(y);
return 'Done';
}
asyncAwait().then((result) => console.log(result));
