DEV Community

Cover image for Typescript Async/Await Internals
John Peters
John Peters

Posted on • Updated on

Typescript Async/Await Internals

"use strict";

var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(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) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: 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) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
Enter fullscreen mode Exit fullscreen mode

This is the actual JavaScript implementation of the Typescript async/await construct.

The _awaiter, simply returns a Promise which is created in compiled JavaScript like this:

 function clickLinkByText(text) {
        return __awaiter(this, void 0, void 0, function () {
            var link;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, getFirstLinkByText(text)];
                    case 1:
                        link = _a.sent();
                        link.click();
                        return [2 /*return*/, link];
                }
            });
        });
    }
Enter fullscreen mode Exit fullscreen mode

This code returns a promise which encapsulates a *generator function (a function which Yields something). In this case it Yields the array of (step, function). Why the Yields? Because JavaScript runtime is single threaded, the *generation function relinquishes control at Yield time, allowing the "main thread" to continue processing. In other words, each time a new "step" is called from the awaiter, the _generation Yields something regarding work flow.

The Typescript code was:

  async function clickLinkByText(text) {
      let link = await getFirstLinkByText(text);
      link.click();
      return link;
   }
Enter fullscreen mode Exit fullscreen mode

I like TypeScript and feel this shows us the power it can contain. I'm pretty sure that this implementation was tested very well, and thus made it into the Typescript release. Could you imagine rewritting the every time you wanted an async/await clone? Some people would say just use Promises or Observables. Both would be correct because they all do the same thing. I believe the async/await construct is easiest on the eyes, and immediately understood.

JWP2020

Top comments (0)