Promise / Async / Await

Node.js Promise / Async / Await 同步处理

使用 async / await 可以避免 callback hell,程式可读性也比 Promise 更好,也可以达到同步处理的效果

函式说明

函式变数 说明
Promise.resolve() 回传资料给 then() 通知已执行完成
Promise.reject() 回传资料给 catch() 通知执行失败
Promise.then() 取得 resolve() 回传的资料,执行成功
Promise.catch() 取得 reject() 回传的资料,执行失败
Promise.finally() 整个 Promise 执行结束,不管成功失败,都会呼叫的函式
Promise.all() 确认所有阵列中的 Promise 都有执行完成
Promise.pending 等待
Promise.fulfilled 成功
Promise.rejected 失败
async 告诉这个函式是 Promise 函式,可以使用 then()catch() 去接收处理后的资料
await 只能出现在 async 函式中,会等待指定的 Promise 函式执行完再执行

使用 Promise

建立 Promise 物件

const logAsync = (message, time) => {
    // 建立 Promise 物件,处理异步资讯
    return new Promise((resolve, reject) => {
        if (message && time) {
            // 设定指定时间显示讯息
            setTimeout(() => {
                console.log(message);
                // 执行正常,呼叫 resolve 通知已处理完成
                resolve(`从 resolve 取得:${message}`)
            }, time);
        } else {
            // 处理异常,呼叫 reject 通知处理错误
            reject(`无法处理 reject: ${message} & ${time}`);
        }
    });
};

使用 then() 取得 Promise 呼叫的 resolve

logAsync('这个讯息过 1 秒才会出现', 1000)
    .then((log1_resolve_message) => {
        console.log(log1_resolve_message);
        return logAsync('这个讯息再过 1.5 秒才会出现', 1500);
    })
    .then((log2_resolve_message) => {
        console.log(log2_resolve_message);
        return logAsync('这个讯息再过 2 秒才会出现', 2000);
    }).then((log3_resolve_message) => {
        console.log(log3_resolve_message);
    });

// 这个讯息过 1 秒才会出现
// 从 resolve 取得:这个讯息过 1 秒才会出现
// 这个讯息再过 1.5 秒才会出现
// 从 resolve 取得:这个讯息再过 1.5 秒才会出现
// 这个讯息再过 2 秒才会出现
// 从 resolve 取得:这个讯息再过 2 秒才会出现

使用 async / await

  • awaitasync function 裡面才可以使用
const logMessage = async () => {
    let log1_resolve_message = await logAsync('1 秒后会出现这句', 1000);
    console.log(log1_resolve_message);
    let log2_resolve_message = await logAsync('再 1.5 秒后会出现这句', 1500);
    console.log(log2_resolve_message);
    let log3_resolve_message = await logAsync('再 2 秒后会出现这句', 2000);
    console.log(log3_resolve_message);
};

logMessage();

// 这个讯息过 1 秒才会出现
// 从 resolve 取得:这个讯息过 1 秒才会出现
// 这个讯息再过 1.5 秒才会出现
// 从 resolve 取得:这个讯息再过 1.5 秒才会出现
// 这个讯息再过 2 秒才会出现
// 从 resolve 取得:这个讯息再过 2 秒才会出现

使用 await Promise.all() 执行

  • awaitasync function 裡面才可以使用
(async () => {
    let allLogMessage = await Promise.all([
        logAsync('1 秒后会出现这句', 1000),
        logAsync('再 1.5 秒后会出现这句', 1500),
        logAsync('再 2 秒后会出现这句', 2000)
    ]);
    console.log(allLogMessage);
})();

// 1 秒后会出现这句
// 再 1.5 秒后会出现这句
// 再 2 秒后会出现这句
// [
//   '从 resolve 取得:1 秒后会出现这句',
//   '从 resolve 取得:再 1.5 秒后会出现这句',
//   '从 resolve 取得:再 2 秒后会出现这句'
// ]

全部在 Promise 中 resolve 的讯息,会用阵列的方式传给 await 前面的变数

使用 catch 抓取 reject() 传的错误讯息

在第 2 次呼叫 1.5 秒时,故意没有传秒数,导致程式发生错误呼叫 reject

logAsync('这个讯息过 1 秒才会出现', 1000)
    .then((log1_resolve_message) => {
        console.log(log1_resolve_message);
        // 在这裡故意没有传秒数,导致程式发生错误呼叫 reject
        return logAsync('这个讯息再过 1.5 秒才会出现');
    })
    .then((log2_resolve_message) => {
        console.log(log2_resolve_message);
        return logAsync('这个讯息再过 2 秒才会出现', 2000);
    })
    .then((log3_resolve_message) => {
        console.log(log3_resolve_message);
    })
    .catch((error) => {
        console.log(error);
    });

// 这个讯息过 1 秒才会出现
// 从 resolve 取得:这个讯息过 1 秒才会出现
// 无法处理 reject: 这个讯息再过 1.5 秒才会出现 & undefined

这裡会发现 第 1 秒 有处理,但 1.5 秒 时就发生错误,后续的 2 秒 就没执行了

使用 Promise.all() catch 抓取 reject() 传的错误讯息

(async () => {
    let allLogMessage = await Promise.all([
        logAsync('1 秒后会出现这句', 1000),
        logAsync('再 1.5 秒后会出现这句'),
        logAsync('再 2 秒后会出现这句', 2000)
    ]);

    console.log('----');
    console.log(allLogMessage);
    console.log('----');
})().catch((error) => {
    console.log(error);
});

// 无法处理 reject: 再 1.5 秒后会出现这句 & undefined
// 1 秒后会出现这句
// 再 2 秒后会出现这句

在这裡发现,再 1.5 秒的时候直接 catch 到错误,直接显示错误讯息

1 秒2 秒 的部分皆有执行,所以表示所有的工作都是异步执行的

只是因为有任一个发生错误,所以 allLogMessage 就没有取得讯息,直接没有执行

将 Async 包成一个变数

const logMesssageAsync = async () => {
    let allLogMessage = await Promise.all([
        logAsync('1 秒后会出现这句', 1000),
        logAsync('再 2 秒后会出现这句'),
        logAsync('再 2 秒后会出现这句', 2000)
    ]);

    console.log('----');
    console.log(allLogMessage);
    console.log('----');
};

let getLogMessageAsync = logMesssageAsync()
    .then((success_message) => {
        console.log(success_message)
    }).catch((error) => {
        console.log(error);
    });

console.log(getLogMessageAsync);

// Promise { <pending> }
// 无法处理 reject: 再 2 秒后会出现这句 & undefined
// 1 秒后会出现这句
// 再 2 秒后会出现这句

因为 logMesssageAsync 被告之为 async 函式,所以在印出执行后的结果会拿到一个 Promise 物件 Promise { <pending> },就可以把这个变数当作 Promise 方式去操作

所以也可以写成这样,会得到一样的讯息

let getLogMessageAsync = logMesssageAsync();

console.log(getLogMessageAsync);

getLogMessageAsync
    .then((success_message) => {
        console.log(success_message)
    }).catch((error) => {
        console.log(error);
    });
// Promise { <pending> }
// 无法处理 reject: 再 2 秒后会出现这句 & undefined
// 1 秒后会出现这句
// 再 2 秒后会出现这句

Promise 链

一直不断使用 then() 可以不断处理上一个 then() 处理的资料

new Promise(function(resolve, reject) {

    setTimeout(() => resolve(1), 1000); // (*)

}).then(function(result) { // (**)

    console.log(result); // 1
    return result * 2;

}).then(function(result) { // (***)

    console.log(result); // 2
    return result * 2;

}).then(function(result) {

    console.log(result); // 4
    return result * 2;

});

Promise / Async / Await

在迴圈使用 async / await

1. 使用 for await…of,每个 Promise 结束后可以马上取得结果


const sleep = (miniSecond) => {
    return new Promise((resolve, reject) => {
        console.log(`[sleep 秒数 - ${miniSecond}] enter`);
        setTimeout(() => {
            console.log(`[sleep 秒数 - ${miniSecond}] resolve`);
            resolve(`秒数 ${miniSecond} 处理结果`);
        }, miniSecond);
    });
};

// 所有的 Promise 会在此时就发动
const SleepPromises = [sleep(1000), sleep(5000), sleep(2000)];

// 方法一:使用 for await,每个 Promise 结束后可以马上取得结果
(async function () {
    for await (const item of SleepPromises) {
        console.log('[for await 结果] ', item);
    }
})();

// [sleep 秒数 - 1000] enter
// [sleep 秒数 - 5000] enter
// [sleep 秒数 - 2000] enter
// [sleep 秒数 - 1000] resolve
// [for await 结果]  秒数 1000 处理结果
// [sleep 秒数 - 2000] resolve
// [sleep 秒数 - 5000] resolve
// [for await 结果]  秒数 5000 处理结果
// [for await 结果]  秒数 2000 处理结果

依序执行的顺序是 100050002000 毫秒

上面可以看到,秒数 2000 的已经优先 resolve 了,但是 for await 在等第 2 个 5000 的结果处理完,才去处理 2000 的结果

2. 使用 for…of,每个 Promise 结束后可以马上取得结果

// 使用 for...of,每个 Promise 结束后可以马上取得结果
(async function () {
    for (const item of SleepPromises) {
        // 等待处理结果
        const result = await item;
        console.log('[for of 结果] ', result);
    }
})();

// [sleep 秒数 - 1000] enter
// [sleep 秒数 - 5000] enter
// [sleep 秒数 - 2000] enter
// [sleep 秒数 - 1000] resolve
// [for of 结果]  秒数 1000 处理结果
// [sleep 秒数 - 2000] resolve
// [sleep 秒数 - 5000] resolve
// [for of 结果]  秒数 5000 处理结果
// [for of 结果]  秒数 2000 处理结果

await 写在 for 迴圈中,一样会依序等待处理结果

3. 使用 for 迴圈,每个 Promise 结束后可以马上取得结果

// 使用 for 迴圈,每个 Promise 结束后可以马上取得结果
(async function () {
    for (let i = 0; i < SleepPromises.length; i++) {
        // 等待处理结果
        const result = await SleepPromises[i];
        console.log('[for] 结果', result);
    }
})();

// [sleep 秒数 - 1000] enter
// [sleep 秒数 - 5000] enter
// [sleep 秒数 - 2000] enter
// [sleep 秒数 - 1000] resolve
// [for] 结果 秒数 1000 处理结果
// [sleep 秒数 - 2000] resolve
// [sleep 秒数 - 5000] resolve
// [for] 结果 秒数 5000 处理结果
// [for] 结果 秒数 2000 处理结果

4. 使用 Promise.all,一次取得所有结果,需等待所有 Promise resolve 或其中一个被 reject 时终止

// 使用 Promise.all,一次取得所有结果,需等待所有 Promise resolve 或其中一个被 reject 时终止
(async function () {
    const results = await Promise.all(SleepPromises);
    console.log('[Promise.all] 结果', results);
})();
// [sleep 秒数 - 1000] enter
// [sleep 秒数 - 5000] enter
// [sleep 秒数 - 2000] enter
// [sleep 秒数 - 1000] resolve
// [sleep 秒数 - 2000] resolve
// [sleep 秒数 - 5000] resolve
// [Promise.all] 结果 [ '秒数 1000 处理结果', '秒数 5000 处理结果', '秒数 2000 处理结果' ]

5. 使用 Promise.allSettled,一次取得所有结果,需等待所有 Promise 都 resolve(fulfilled) / reject(rejected) 后终止

// 使用 Promise.allSettled,一次取得所有结果,需等待所有 Promise 都 resolve(fulfilled) / reject(rejected) 后终止
(async function () {
    const results = await Promise.allSettled(SleepPromises);
    console.log('[Promise.allSettled] 结果', results);
})();
// [sleep 秒数 - 1000] enter
// [sleep 秒数 - 5000] enter
// [sleep 秒数 - 2000] enter
// [sleep 秒数 - 1000] resolve
// [sleep 秒数 - 2000] resolve
// [sleep 秒数 - 5000] resolve
// [Promise.allSettled] 结果 [
//   { status: 'fulfilled', value: '秒数 1000 处理结果' },
//   { status: 'fulfilled', value: '秒数 5000 处理结果' },
//   { status: 'fulfilled', value: '秒数 2000 处理结果' }
// ]

6. 使用 for…of await,一次取得所有结果,需等待所有 Promise resolve 或其中一个被 reject 时终止

不如就用 Promise.all 吧

// 使用 for...of await,一次取得所有结果,需等待所有 Promise resolve 或其中一个被 reject 时终止 吧)
(async function () {
    for (const result of await Promise.all(SleepPromises)) {
        console.log('[for...of await] 结果', result);
    }
})();
// [sleep 秒数 - 1000] enter
// [sleep 秒数 - 5000] enter
// [sleep 秒数 - 2000] enter
// [sleep 秒数 - 1000] resolve
// [sleep 秒数 - 2000] resolve
// [sleep 秒数 - 5000] resolve
// [for...of await] 结果 秒数 1000 处理结果
// [for...of await] 结果 秒数 5000 处理结果
// [for...of await] 结果 秒数 2000 处理结果

参考资料