Async/Await with Decorator for more elegant asynchronous programming

As Async/Await is officially incorporated into ES7 standard, more and more people are using this so-called asynchronous programming end level solution. However, the more await expressions are found in the process of use, the more redundant code is used to create Promise objects, and the sense of elegance plummets. For this reason, this paper combines Decorator to realize more elegant asynchronous programming.

Use

  1. Declare an asynchronous function through async keyword;
  2. The await keyword is only valid in async functions and expects a Promise object. The await expression pauses the execution of the current async function, waiting for Promise to finish processing and return the result.

Example:

class Action {
    step_1(value){
        return new Promise((resolve, reject) => {   // Return Promise object
            console.log('[step_1]', value);
            resolve();
        });
    }
    step_2(value){
        return new Promise((resolve, reject) => {
            console.log('[step_2]', value);
            resolve();
        });
    }
    step_n(value){
        return new Promise((resolve, reject) => {
            console.log('[step_n]', value);
            resolve();
        });
    }
}

// async self executing function
(async ()=>{
    let action = new Action();
    await action.step_1('one');    // => [step_1] one
    await action.step_2('two');    // => [step_2] two
    await action.step_n('next');   // => [step_n] next
})();

Improvement

As you can see above, each step method needs to return a Promise object and execute asynchronous code in the Promise callback function.
However, such a way of writing is not elegant at all, and the more await expressions, the more code to create Promise objects, which is very redundant.

Decorator

Decorator can annotate and modify classes or properties.
With this feature, the redundant code for creating Promise object can be encapsulated as a decorator and applied to step method.

/**
 * Decorator: Promise
 * @param {object} target - Decorated class
 * @param {string} property - Attribute name
 * @param {object} descriptor - property descriptor 
 * @author Brother range < ambit_tsai@qq.com >
 */
function promisify(target, property, descriptor){
    descriptor.value = new Proxy(descriptor.value, {
        // Intercept function calls
        apply(method, context, argList){
            // Returns a new Promise object
            return new Promise((resolve, reject) => {
                // Call the original function and inject resolve and reject
                return Reflect.call(method, context, resolve, reject, ...argList);
            });
        }
    });
    return descriptor;
}

(Note: Decorator has not been included in the ECMAScript standard, but syntax support can be provided through the Babel compiler.)

Code optimization

class Action {
    @promisify
    step_1(resolve, reject, value){
        console.log('[step_1]', value);
        resolve();
    }
    @promisify
    step_2(resolve, reject, value){
        console.log('[step_2]', value);
        resolve();
    }
    @promisify
    step_n(resolve, reject, value){
        console.log('[step_n]', value);
        resolve();
    }
}

(Note: due to function declaration promotion, Decorator can only be used for classes and methods.)

Tags: Programming Attribute ECMAScript

Posted on Thu, 13 Feb 2020 13:14:52 -0500 by rodolp13