亚洲日本永久一区二区_国产精品k频道网址导航_首页aⅴ色老汉中文字幕_免费深夜全片观看_9久久9毛片又大又硬又粗_国产精品成亚洲电影_日韩不用播放器的av_欧美特色特黄视频

在 JavaScript 中編寫Go式錯誤處理的async/await

在 JavaScript 中編寫Go式錯誤處理的async/await

ES7 引入 async/await 允許開發人員編寫看起來像同步的異步 JavaScript 代碼。在當前的 JavaScript 版本中,還可以使用 Promises,這些功能都是為了簡化異步流程并避免回調地獄。

回調地獄是一個術語,用于描述 JavaScript 中的以下情況:

function asyncTasks() {
    asyncFuncA(function (err, resultA) {
        if (err) return cb(err);

        asyncFuncB(function (err, resultB) {
            if (err) return cb(err);

            asyncFuncC(function (err, resultC) {
                if (err) return cb(err);

                // 更多...
            });
        });
    });
}

上述代碼使得維護代碼和管理控制流變得非常困難。只需考慮一個 if 語句,如果 callbackA 的某些結果等于 foo,則需要執行其他 async 方法。

拯救 Promises

借助 promisesES6,可以將之前代碼的回調噩夢簡化為如下內容:

function asyncTasks(cb) {
    asyncFuncA
        .then(AsyncFuncB)
        .then(AsyncFuncC)
        .then(AsyncFuncD)
        .then((data) => cb(null, data))
        .catch((err) => cb(err));
}

雖然從代碼閱讀來看好很多。但在現實世界的場景中,異步流程可能會變得稍微復雜一些,例如在服務器模型 (nodejs 編程)中,可能想要將一個實體數據保存到數據庫中,然后根據保存的值查詢其他一些實體,如果該值存在,執行其他一些異步任務,在所有任務完成后,可能想要用步驟 1 中創建的對象響應用戶。如果在某個步驟中發生了錯誤,希望通知用戶確切的錯誤。

當然,使用 promises 看起來會比使用普通回調更干凈,但它仍然會變得有點混亂。

當然,使用 promises 看起來會比使用普通回調更干凈,但它仍然會變得有點混亂。

關于 Promise 需要的了解的:

async/await

在 ECMAScript 2017 中添加了 async/await 關鍵字,并在主流腳本庫和其他 JavaScript 編程中得到廣泛的應用。

這就是 async/await 真正有用的地方,通過它可以編寫下面的代碼:

async function asyncTasks(cb) {
    const user = await UserModel.findById(1);
    if (!user) return cb("用戶未找到");

    const savedTask = await TaskModel({ userId: user.id, name: "DevPoint" });

    if (user.notificationsEnabled) {
        await NotificationService.sendNotification(user.id, "任務已創建");
    }

    if (savedTask.assignedUser.id !== user.id) {
        await NotificationService.sendNotification(
            savedTask.assignedUser.id,
            "任務已為您創建"
        );
    }

    cb(null, savedTask);
}

上面的代碼看起來干凈多了,但是錯誤處理還是存在不足。

進行異步調用時,在執行 promise 期間可能會發生某些事情(如數據庫連接錯誤、數據庫模型驗證錯誤等)。由于 async 函數正在等待 Promise,因此當 Promise 遇到錯誤時,它會拋出一個異常,該異常將在 Promisecatch 方法中被捕獲。

async/await 函數中,通常使用 try/catch 塊來捕獲此類錯誤。

使用 try/catch 后代碼如下:

async function asyncTasks(cb) {
    try {
        const user = await UserModel.findById(1);
        if (!user) return cb("用戶未找到");
    } catch (error) {
        return cb("程序異常:可能是數據庫問題");
    }

    try {
        const savedTask = await TaskModel({
            userId: user.id,
            name: "DevPoint",
        });
    } catch (error) {
        return cb("程序異常:任務保存失敗");
    }

    if (user.notificationsEnabled) {
        try {
            await NotificationService.sendNotification(user.id, "任務已創建");
        } catch (error) {
            return cb("程序異常:sendNotification 失敗");
        }
    }

    if (savedTask.assignedUser.id !== user.id) {
        try {
            await NotificationService.sendNotification(
                savedTask.assignedUser.id,
                "任務已為您創建"
            );
        } catch (error) {
            return cb("程序異常:sendNotification 失敗");
        }
    }

    cb(null, savedTask);
}

個人對 try/catch 的使用不太喜歡,總覺得這種代碼的使用是用于捕獲無法預知的錯誤,比較喜歡 Go 的處理方式,當然這純屬個人觀點。

優化 try/catch

在 Go 中的處理方式如下:

data, err := db.Query("SELECT ...")
if err != nil { return err }

它比使用 try/catch 塊更干凈,并且更少地聚集代碼,可讀和可維護性更高。

但是 await 的問題在于,如果沒有為其提供 try/catch 塊,它會靜默退出函數。除非提供 catch 子句,否則將無法控制它。

利用 await 是在等待 resolvepromise。下面可以制作小的效用函數來捕獲這些錯誤:

function to(promise) {
    return promise
        .then((data) => {
            return [null, data];
        })
        .catch((err) => [err]);
}

效用函數接收一個 promise,然后將成功響應解析為一個數組,并將返回數據作為第二項,并且從捕獲中收到的錯誤是第一個。

function to(promise) {
    return promise
        .then((data) => {
            return [null, data];
        })
        .catch((err) => [err]);
}

async function asyncTask() {
    let err, user, savedTask;

    [err, user] = await to(UserModel.findById(1));
    if (!user) throw new CustomerError("用戶未找到");

    [err, savedTask] = await to(
        TaskModel({ userId: user.id, name: "DevPoint" })
    );
    if (err) throw new CustomError("程序異常:任務保存失敗");

    if (user.notificationsEnabled) {
        const [err] = await to(
            NotificationService.sendNotification(user.id, "任務已創建")
        );
        if (err) console.error("程序異常");
    }
}

上面的示例只是該解決方案的一個簡單用例,還可以在 to 方法中增加一個攔截器,接收原始錯誤對象,記錄它或在將它傳回之前做任何需要做的事情。