В качестве мысленного эксперимента представьте себе: способ сказать среде выполнения JavaScript приостановить выполнение кода по ключевому слову await при использовании вместе с промисом и возобновить только единожды (и если) промис выполнен:
Когда промис завершён, выполнение функции продолжается,
если он исполнен, то await вернет значение,
если он отклонен, синхронно будет выброшена ошибка, которую мы можем отловить.
Это неожиданно (и волшебным образом) делает асинхронное программирование таким же простым, как синхронное программирование. Для этого мысленного эксперимента необходимы три вещи:
Возможность приостановить выполнение функции.
Возможность поместить значение внутри функции.
Возможность бросить исключение внутри функции.
Это именно то, что генераторы позволили нам сделать! Мысленный эксперимент на самом деле реален, как и реализация async/await в TypeScript / JavaScript. Под капотом просто используются генераторы.
где wrapToReturnPromise просто выполняет функцию генератора для получения generator и затем использует generator.next(), если значение является promise, то оно выполнит then + catch промиса и в зависимости от результата вызовет generator.next(result) или generator.throw(error). Вот и всё!
Async - Await поддерживается TypeScript, начиная с версии 1.7. Асинхронные функции начинаются с ключевого слова async; await приостанавливает выполнение, пока не будет получено значение из Promise.
Ранее async / await поддерживался только в es6, и транспилировался непосредственно в ES6 generators.
TypeScript 2.1добавлена возможность выполнения в ES3 и ES5, что означает, что вы можете использовать async / await независимо от того, какую среду вы используете. Важно отметить, что мы можем использовать async / await с TypeScript 2.1, и многие браузеры их поддерживают, конечно, сперва добавив глобально полифилл для Promise.
Давайте посмотрим этот пример и код, чтобы выяснить, как работает TypeScript async / await нотация:
1 2 3 4 5 6 7 8 910111213141516171819202122232425
functiondelay(milliseconds:number,count:number):Promise<number>{returnnewPromise<number>((resolve)=>{setTimeout(()=>{resolve(count);},milliseconds);});}// асинхронная функция всегда возвращает PromiseasyncfunctiondramaticWelcome():Promise<void>{console.log('Hello');for(leti=0;i<5;i++){// await преобразует Promise<number> в числоconstcount:number=awaitdelay(500,i);console.log(count);}console.log('World!');}dramaticWelcome();
var__awaiter=(this&&this.__awaiter)||function(thisArg,_arguments,P,generator){returnnew(P||(P=Promise))(function(resolve,reject){functionfulfilled(value){try{step(generator.next(value));}catch(e){reject(e);}}functionrejected(value){try{step(generator['throw'](value));}catch(e){reject(e);}}functionstep(result){result.done?resolve(result.value):newP(function(resolve){resolve(result.value);}).then(fulfilled,rejected);}step((generator=generator.apply(thisArg,_arguments||[])).next());});};functiondelay(milliseconds,count){returnnewPromise((resolve)=>{setTimeout(()=>{resolve(count);},milliseconds);});}// асинхронная функция всегда возвращает PromisefunctiondramaticWelcome(){return__awaiter(this,void0,void0,function*(){console.log('Hello');for(leti=0;i<5;i++){// await преобразует Promise <число> в числоconstcount=yielddelay(500,i);console.log(count);}console.log('World!');});}dramaticWelcome();
var__awaiter=(this&&this.__awaiter)||function(thisArg,_arguments,P,generator){returnnew(P||(P=Promise))(function(resolve,reject){functionfulfilled(value){try{step(generator.next(value));}catch(e){reject(e);}}functionrejected(value){try{step(generator['throw'](value));}catch(e){reject(e);}}functionstep(result){result.done?resolve(result.value):newP(function(resolve){resolve(result.value);}).then(fulfilled,rejected);}step((generator=generator.apply(thisArg,_arguments||[])).next());});};var__generator=(this&&this.__generator)||function(thisArg,body){var_={label:0,sent:function(){if(t[0]&1)throwt[1];returnt[1];},trys:[],ops:[],},f,y,t,g;return((g={next:verb(0),throw:verb(1),return:verb(2),}),typeofSymbol==='function'&&(g[Symbol.iterator]=function(){returnthis;}),g);functionverb(n){returnfunction(v){returnstep([n,v]);};}functionstep(op){if(f)thrownewTypeError('Генератор уже выполняется.');while(_)try{if(((f=1),y&&(t=y[op[0]&2?'return':op[0]?'throw':'next'])&&!(t=t.call(y,op[1])).done))returnt;if(((y=0),t))op=[0,t.value];switch(op[0]){case0:case1:t=op;break;case4:_.label++;return{value:op[1],done:false,};case5:_.label++;y=op[1];op=[0];continue;case7:op=_.ops.pop();_.trys.pop();continue;default:if(!((t=_.trys),(t=t.length>0&&t[t.length-1]))&&(op[0]===6||op[0]===2)){_=0;continue;}if(op[0]===3&&(!t||(op[1]>t[0]&&op[1]<t[3]))){_.label=op[1];break;}if(op[0]===6&&_.label<t[1]){_.label=t[1];t=op;break;}if(t&&_.label<t[2]){_.label=t[2];_.ops.push(op);break;}if(t[2])_.ops.pop();_.trys.pop();continue;}op=body.call(thisArg,_);}catch(e){op=[6,e];y=0;}finally{f=t=0;}if(op[0]&5)throwop[1];return{value:op[0]?op[1]:void0,done:true,};}};functiondelay(milliseconds,count){returnnewPromise(function(resolve){setTimeout(function(){resolve(count);},milliseconds);});}// асинхронная функция всегда возвращает PromisefunctiondramaticWelcome(){return__awaiter(this,void0,void0,function(){vari,count;return__generator(this,function(_a){switch(_a.label){case0:console.log('Hello');i=0;_a.label=1;case1:if(!(i<5))return[3/*break*/,4];return[4/*yield*/,delay(500,i)];case2:count=_a.sent();console.log(count);_a.label=3;case3:i++;return[3/*break*/,1];case4:console.log('World!');return[2/*return*/];}});});}dramaticWelcome();
Для обоих сценариев применения нам необходимо убедиться, что наш исполняемый код имеет глобальный доступ к ECMAScript-совместимому Promise. Как вариант это может быть использование полифилла для Promise. Мы также должны убедиться, что TypeScript знает, что Promise поддерживается, установив для нашего флага lib что-то вроде «dom», «es2015» или «dom», «es2015.promise», «es5».
Мы можем увидеть, какие браузеры имеют поддержку Promise (нативную и заполифилленную) здесь.