TS Option类型与Promise

 用专门的数据类型描述异常,能便于串联可能出错的操作。Option类型就是这张容器,在没有值时也能串联操作。

js中常用Promise来处理异步调用,本文将手动实现Promise的部分功能,来探索这个类型的处理逻辑。

1 处理错误

TS表示和处理错误的常用模式有:

1)返回null。

2)抛出异常。

3)返回异常。

4)Option类型。

这些处理机制各有千秋,可根据实际的业务场景来选择使用。

1.1 处理错误的常规模式

1.1.1 返回null

在遇到错误时,直接返回null,其他情况下返回正常的值。

function parseDate() {
    let num = Math.random();
    if (num > 0.5) {  // 模拟遇到错误的场景
        return null;
    }
    return new Date();
}

在调用此类函数时,需要先检查它的返回结果,然后再使用。

let result = parseDate();
if (result) {
    console.log(result);
} else {
    console.log("出错啦");
}

这种方式是处理错误最为轻量的方式,但是无法返回出错原因,且返回的null也不利于程序的编写,每次操作都需要检查返回结果是否为null太繁琐,不利于嵌套和串联操作。

1.1.2 抛出异常

当遇到错误时直接抛出异常。

function parseDate() {
    let num = Math.random();
    if (num > 0.5) {  // 模拟遇到错误的场景
        throw new RangeError("随机数值小于0.5");
    }
    return new Date();
}

在调用此类函数时,需要使用try/catch来捕获错误。

try {
    console.log(parseDate());
} catch (error) {
    console.error(error);
}

这种方式可以指出失败原因,但是需要使用try/catch来捕获。这样在串联或嵌套操作时将会使代码结构变得复杂。而在实际开发中,开发人员可能不会将代码放在try/catch中,不会去检查异常。

1.1.3 返回异常

在遇到错误时,直接return 异常。

function parseDate() {
    let num = Math.random();
    if (num > 0.5) {  // 模拟遇到错误的场景
        return  new RangeError("随机数值小于0.5");
    }
    return new Date();
}

在调用此类函数时,需要根据返回值类型做不同的处理。

let result = parseDate();
if (result instanceof Error) {
    console.error("出错了:" + result.message);
} else {
    console.log(result);
}

这种方式比较轻量,又能提供错误信息,能强制使用方处理每一个异常。但是在串联和嵌套操作时会比较繁琐。

1.2 Option类型

Option不是JS内置的数据类型,需要我们自己编写。它表示不返回一个值,而是返回一个容器。该容器可能有一个值,也可能没有。这个容器有一些方法,让没有值时也能串联操作。

class CustomOption {

    constructor(private val: T) {
    }

    then(fun: (val:T) => CustomOption) {
        return fun(this.val);
    }

    getValue(errorMessage: string) {
        return this.val || errorMessage;
    }
}

function askInput() {
    let num = Math.random();
    if (num > 0.5) {
        return new CustomOption(null)
    }
    return new CustomOption("2023-12-16 12:34:24");
}

function parseDate(str: string | null) {
    if (str) {
        return new CustomOption(new Date(str));
    } else {
        return new CustomOption(null);
    }
}

let result = askInput().then(parseDate).getValue("转化失败");
console.log(result);

上面代码有两个问题:1)实际上当第一个函数报错的时候就意味着整个调用链都出错了,因此在后面的调用上,应该及时知晓上个返回值是个空值,以免执行不必要的操作。2)在调用getValue这个参数时,当前一个函数返回正常值时是不需要往getValue传递参数的。

下面是优化后的代码:

interface CustomOption {
    then(fun: (val:T) => CustomOption): CustomOption;
    getValue(val:T):T;
}

class CustomSome implements CustomOption {
    constructor(private value: T) {
    }

    getValue(): T {
        return this.value;
    }

    then(fun: (val:T) => CustomOption): CustomOption {
        return fun(this.value);
    }
}

class CustomNone implements CustomOption {
    getValue(val: T): T {
        return val;
    }

    then(): CustomOption {
        return this;
    }
}

function askInput() {
    let num = Math.random();
    if (num > 0.5) {
        return new CustomNone()
    }
    return new CustomSome("2023-12-16 12:34:24");
}

function parseDate(str: string | null) {
    if (str) {
        return new CustomSome(new Date(str));
    } else {
        return new CustomNone();
    }
}

let result = askInput().then(parseDate).getValue("转化失败");
console.log(result);

2 处理回调

JS异步程序的核心基础是回调。回调其实就是常规函数,只是作为参数传给另一个函数。就像在同步程序一样,另一个函数在操作完成后调用会调函数。

但在串联和嵌套操作中,容易导致“回调金字塔”。

function askInput(callback: (val: string) => void) {
    let num = Math.random();
    if (num > 0.5) {
        throw new RangeError("随机数小于0.5");
    } else {
        callback("2023-12-13 12:00");
    }
}

function parseDate(str: string,callback: (date: Date) => void) {
   callback(new Date(str));
}

askInput((val: string) => { // 嵌套第一层
    if (val) {
        parseDate(val,(date:Date) => { // 嵌套第二层
            console.log(date);
        })
    }
});

2.1 Promise

在JS中,常用Promise来解决“回调金字塔”问题。

1)实现Promise的then方法来处理回调。

type Executor = (
    resolve: ExecutorResolve,
    reject: ExecutorReject
) => void

type CustomPromiseStatus = "pending" | "finished" | "rejected";
type ExecutorResolve = (val: any)=>void;
type ExecutorReject = (error: any) =>void;

class CustomPromise {

    private status: CustomPromiseStatus;
    private value: any;
    private error: null;
    private resolveCallbackList: Array;
    private rejectCallbackList: Array;

    constructor(executor: Executor) {
        this.status = "pending";
        this.value = null;
        this.error = null;
        this.resolveCallbackList = [];
        this.rejectCallbackList = [];

        const resolve:ExecutorResolve = (val: any) => {
            this.status = "finished";
            this.value = val;
            try {
                this.resolveCallbackList.forEach(callback => callback(this.value));
            } catch (e) {
                reject(e);
            }
        };

        const reject:ExecutorReject = (error: any) => {
            this.status = "rejected";
            this.error = error;
            this.rejectCallbackList.forEach(callback => callback(error));
        }
        executor(resolve,reject);
    }

    then(resolve:ExecutorResolve,reject: ExecutorReject) {
        switch (this.status) {
            case "finished":
                try {
                    resolve(this.value);
                } catch (e) {
                    reject(e);
                }
                break;
            case "rejected":
                reject(this.error);
                break;
            default:
                this.resolveCallbackList.push(resolve);
                this.rejectCallbackList.push(reject);
        }
    }
}

function askInputPromise() {
    return new CustomPromise((resolve, reject) => {
        let num = Math.random();
        if (num > 0.5) {
            reject(new RangeError("随机数小于0.5"));
        } else {
            resolve("2023-12-16 12:34:24");
        }
    })
}

let promise = askInputPromise();
promise.then((val:string) => {
    console.log(new Date(val));
},(error) => {
    console.log("出错啦:" + error);
})

2)then 方法返回Promise类型,实现可串联使用。

type Executor = (
    resolve: ExecutorResolve,
    reject: ExecutorReject
) => void

type CustomPromiseStatus = "pending" | "finished" | "rejected";
type ExecutorResolve = (val: any)=> any;
type ExecutorReject = (error: any) =>void;

let IsPromise = (val: any) : val is CustomPromise => {
    return val instanceof CustomPromise;
};

class CustomPromise {

    private status: CustomPromiseStatus;
    private value: any;
    private error: null;
    private resolveCallbackList: Array;
    private rejectCallbackList: Array;

    constructor(executor: Executor) {
        this.status = "pending";
        this.value = null;
        this.error = null;
        this.resolveCallbackList = [];
        this.rejectCallbackList = [];

        const resolve:ExecutorResolve = (val: any) => {
            this.status = "finished";
            this.value = val;
            try {
                this.resolveCallbackList.forEach(callback => callback(this.value));
            } catch (e) {
                reject(e);
            }
        };

        const reject:ExecutorReject = (error: any) => {
            this.status = "rejected";
            this.error = error;
            this.rejectCallbackList.forEach(callback => callback(error));
        }
        executor(resolve,reject);
    }

    then(resolve:ExecutorResolve,reject?: ExecutorReject) {

        return new CustomPromise((resolveNew, rejectNew) => {

            const resolveFun:ExecutorResolve = (val: any) => {
                try {
                    let res = resolve(this.value);
                    if (IsPromise(res)) {
                        res.then(resolveNew);
                    } else {
                        resolveNew(res);
                    }
                } catch (e) {
                    rejectNew(e);
                }
            };

            const rejectFun: ExecutorReject = (error: any) => {
                if (reject) {
                    reject(error);
                }
                if (rejectNew) {
                    rejectNew(error);
                }
            }

            switch (this.status) {
                case "finished":
                    try {
                        resolveFun(this.value);
                    } catch (e) {
                        rejectFun(e);
                    }
                    break;
                case "rejected":
                    rejectFun(this.error);
                    break;
                default:
                    this.resolveCallbackList.push(resolveFun);
                    this.rejectCallbackList.push(rejectFun);
            }
        })
    }
}

function askInputPromise() {
    return new CustomPromise((resolve, reject) => {
        let num = Math.random();
        if (num > 0.5) {
            reject(new RangeError("随机数小于0.5"));
        } else {
            resolve("2023-12-16 12:34:24");
        }
    })
}

askInputPromise().then((val:string) => {
    return new CustomPromise((resolve, reject) => {
        console.log("输入参数:" + val);
        resolve(new Date(val));
    })
}).then((val:any) => {
    console.log("最终结果:",val)
},(error: any) => {
    console.log("在这里就出错了",error);
});

你可能感兴趣的:(TypeScript编程,javascript,前端,vue.js,typescript)