Typescript Asynchronous Programming Paradigm Await/Async Deferred/Promise

Keywords: Programming TypeScript

How can we gracefully implement asynchronous programming in Typescript programming?

Write the upper logic with Await/Async and encapsulate the callback function with Deferred/Promise.

Let's first look at a piece of code in the actual working environment:
This code implements the function of uploading local files or folders to hdfs.

    async upload(localPath: string, hdfsPath: string, recursive = false): Promise<void> {
        if (recursive === true) { // If the upload is a directory, recursive: true
            const fileDescriptors = await io.readdir(localPath); // Read folder name
            const promises = fileDescriptors.map((file: string) => this.fnUpload(file, localPath, hdfsPath)); // Get an array whose element is promise
            await Promise.all(promises);etc. promises All in this array promise Continue to execute backwards after execution
        } else { // If the upload is a file
            await this.pipe(io.createReadStream(localPath), this._hdfsClient.createWriteStream(hdfsPath.substr(6))); // Set up a pipeline to send data
        }
    }
    async fnUpload(file: string, localPath: string, hdfsPath: string): Promise<void> {
        const stats = await io.stat(path.join(localPath, file)); // View the file status of the current path

        if (stats.isDirectory()) { // If it's a directory
            await this.upload(path.join(localPath, file), hdfsPath.concat(file), true);
        } else { // If it's a file
            await this.upload(path.join(localPath, file), hdfsPath.concat(file));
        }
    }

We mainly use async and await as grammatical sugars to achieve asynchrony in a synchronous way of thinking. In the code above, our idea is very natural. If this process wants to be synchronous, then we add an await before this method, then what does this await do for us?

First, let's introduce promise, which has three states: pending, fulfilled, rejected. When a function returns a promise object, it immediately returns a promise in pending state, so it won't block the execution of other code, and the function is also executed asynchronously.

function test () {
    return new Promise(resolve, reject)  {
        console.info('my code'); // Your code
        if (Your code has been executed successfully.) {
            resolve(result); // Pass the result result to resolve ()
        } else {
            reject(); // If your code execution is unsuccessful or an exception occurs
        }
    }
}

test();
console.info('hello, guys');

The above code will output hello, guys, and then my code.

Behind await is a promise (if not promise will convert it to promise. resolution (code). What await needs to do is wait until the promise state becomes full lfilled, and then continue to execute later, which achieves the effect of suspending waiting. There is more asynchrony in the promise behind, just waiting where the upper logic needs to wait. However.
However, await can only be used within the methods declared by async. Why? Let's introduce what async has done.
When you declare the keyword async in function money, the compiler thinks you are an asynchronous function. He will declare a promise for you, and put all the code in your function in a promise, like this:

function test () {
    console.info('in');
};

function test () {
    return new Promise() {
        console.info('in');
    }
}

The advantage is that I guarantee that all await environments are asynchronous and do not block the execution of the upper code.
So what if I want multiple parts of a process to be asynchronous, but the overall process to be synchronous?
Promise.all(array[promise]) is used in our original code. The parameter is an array of promises, which returns a promise, so that you await Promise.all() can wait until all promises in this array are successful before continuing to execute.

With this grammatical sugar, we naturally write out all the asynchronous and synchronous logic. The readability of the code is also very high, but there is a problem at this time. What if my current function is an asynchronous function, but it does not return promise?
For example, many of our common library functions are implemented by callback functions to execute one piece of code and then the other end of the code.

function add (num1, num2, callback) {
    var sum = num1 + num2;
    callback(sum);
}

add(1, 2, function (sum) {
    console.log(sum); // 3
});

But what we want to see is this:

await add(1, 2);
console.log(sum); // 3

How to achieve it?
The answer is to use Deferred/Promise to encapsulate the callback function into a function that returns promise.

function add (num1, num2, callback) {
    var sum = num1 + num2;
    callback(err, sum); // Pretend there might be something unusual coming out here.
}

function add_promise (num1, num2) {
    const deferred = new Deferred<number>();
    add (num1, num2, (err, sum) => {
        if (err) {
            deferred.reject();
        } else {
            deferred.resolve(sum);
        }
    });
    return deferred.promise;
}

const sum = await add_promise(1, 2);
console.log(sum); // 3

Is the code much more readable for upper-level method callers? Also jumped out of the callback hell, through Async/Await, Deferred/Promise cooperation, asynchronous programming this piece, we completely solved.

Posted by ctimmer on Tue, 01 Jan 2019 19:30:09 -0800