异步编程简介
在前端开发中,我们经常需要处理异步操作,例如发送 AJAX 请求、读取本地存储、执行定时任务等等。异步操作与同步操作的不同点在于,异步操作需要在后台执行,并且不会阻塞主线程,使得程序可以同时执行多个任务,提高了程序的性能。
然而,异步编程也带来了一些挑战。异步操作的结果不是立即可用的,需要等待异步操作完成后才能继续执行下一步操作。在传统的回调函数中,这种异步操作的嵌套会导致代码难以维护和理解。因此,ES6 中引入了 Promise 和 async/await 异步编程模式,使异步编程更加简洁、易读和易维护。
Promise
Promise 是 ES6 中引入的一种异步编程模式。它通过链式调用的方式,解决了回调函数嵌套的问题,使得异步代码更加清晰易读。
Promise 的基本使用
Promise 有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。当一个 Promise 被创建时,它处于 pending 状态。当 Promise 成功完成时,它会进入 fulfilled 状态,并将结果传递给 then() 方法;当 Promise 失败时,它会进入 rejected 状态,并将错误传递给 catch() 方法。
下面是一个简单的 Promise 示例,模拟了一个异步操作:
// javascriptcn.com 代码示例 const promise = new Promise((resolve, reject) => { setTimeout(() => { const result = Math.random(); if (result > 0.5) { resolve(result); } else { reject(new Error('Failed')); } }, 1000); }); promise.then((result) => { console.log(`Succeeded: ${result}`); }).catch((error) => { console.error(`Failed: ${error}`); });
在这个示例中,我们创建了一个 Promise,模拟了一个 1 秒钟后返回随机数的异步操作。如果随机数大于 0.5,则 Promise 进入 fulfilled 状态,并将随机数传递给 then() 方法;否则,Promise 进入 rejected 状态,并将错误传递给 catch() 方法。
Promise 的进阶使用
除了基本的 Promise 使用方式外,ES6 还提供了一些高级的 Promise 方法,例如 Promise.all() 和 Promise.race()。
Promise.all()
Promise.all() 方法接收一个 Promise 数组作为参数,返回一个新的 Promise,当所有的 Promise 都成功完成时,它才会进入 fulfilled 状态;当任意一个 Promise 失败时,它就会进入 rejected 状态。如果 Promise 数组中有一个或多个 Promise 失败,Promise.all() 方法返回的 Promise 就会立即进入 rejected 状态,并将第一个失败的 Promise 的错误传递给 catch() 方法。
下面是一个使用 Promise.all() 的示例,模拟了同时发送多个 AJAX 请求的情况:
// javascriptcn.com 代码示例 const urls = ['https://api.github.com/users/defunkt', 'https://api.github.com/users/mojombo', 'https://api.github.com/users/pjhyett']; const promises = urls.map((url) => { return fetch(url).then((response) => response.json()); }); Promise.all(promises).then((results) => { console.log(results); }).catch((error) => { console.error(error); });
在这个示例中,我们定义了一个包含三个 URL 的数组,然后使用 fetch() 方法发送 AJAX 请求,并将结果解析为 JSON 格式。使用 Promise.all() 方法,我们可以同时发送这三个请求,并在所有请求都成功完成后,打印出结果数组。
Promise.race()
Promise.race() 方法和 Promise.all() 方法类似,也接收一个 Promise 数组作为参数,返回一个新的 Promise。不同的是,Promise.race() 方法只要有一个 Promise 成功完成或失败,它就会进入对应的状态,并将结果或错误传递给 then() 或 catch() 方法。
下面是一个使用 Promise.race() 的示例,模拟了一个超时请求的情况:
// javascriptcn.com 代码示例 const promise = new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('Timeout')); }, 5000); }); const timeout = new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('Request is taking too long')); }, 3000); }); Promise.race([promise, timeout]).then((result) => { console.log(result); }).catch((error) => { console.error(error); });
在这个示例中,我们创建了一个 Promise,模拟了一个 5 秒钟后返回结果的异步操作。同时,我们还创建了一个超时 Promise,模拟了一个 3 秒钟后请求超时的情况。使用 Promise.race() 方法,我们可以同时执行这两个 Promise,并在其中一个 Promise 成功完成或失败后,打印出结果或错误信息。
async/await
async/await 是 ES8 中引入的一种异步编程模式。它基于 Promise,使用 async 和 await 关键字,使异步代码更加简洁、易读和易维护。
async/await 的基本使用
async/await 基于 Promise,可以将 Promise 的链式调用转换为类似同步代码的形式。在 async 函数中,我们可以使用 await 关键字等待 Promise 完成,并将结果赋值给变量,这样我们就可以像处理同步代码一样处理异步代码了。
下面是一个简单的 async/await 示例,模拟了一个异步操作:
// javascriptcn.com 代码示例 async function getData() { const promise = new Promise((resolve, reject) => { setTimeout(() => { const result = Math.random(); if (result > 0.5) { resolve(result); } else { reject(new Error('Failed')); } }, 1000); }); try { const result = await promise; console.log(`Succeeded: ${result}`); } catch (error) { console.error(`Failed: ${error}`); } } getData();
在这个示例中,我们定义了一个 async 函数 getData(),在其中创建了一个 Promise,模拟了一个 1 秒钟后返回随机数的异步操作。使用 await 关键字,我们可以等待 Promise 完成,并将结果赋值给变量 result。如果 Promise 成功完成,则打印出成功的信息;否则,打印出失败的信息。
async/await 的进阶使用
除了基本的 async/await 使用方式外,ES8 还提供了一些高级的 async/await 方法,例如 Promise.all() 和 Promise.race()。
Promise.all()
在 async/await 中使用 Promise.all() 方法,可以等待多个 Promise 完成,并将结果作为一个数组返回。与 Promise.all() 方法类似,当其中一个 Promise 失败时,整个 async 函数就会进入 rejected 状态,并将第一个失败的 Promise 的错误传递给 catch() 方法。
下面是一个使用 Promise.all() 的示例,模拟了同时发送多个 AJAX 请求的情况:
// javascriptcn.com 代码示例 async function getData() { const urls = ['https://api.github.com/users/defunkt', 'https://api.github.com/users/mojombo', 'https://api.github.com/users/pjhyett']; const promises = urls.map((url) => { return fetch(url).then((response) => response.json()); }); try { const results = await Promise.all(promises); console.log(results); } catch (error) { console.error(error); } } getData();
在这个示例中,我们定义了一个 async 函数 getData(),在其中定义了一个包含三个 URL 的数组。使用 fetch() 方法发送 AJAX 请求,并将结果解析为 JSON 格式。使用 Promise.all() 方法,我们可以等待这三个请求都完成,并将结果作为一个数组返回。
Promise.race()
在 async/await 中使用 Promise.race() 方法,可以等待多个 Promise 中的第一个完成,并将结果或错误信息作为返回值。与 Promise.race() 方法类似,当其中一个 Promise 失败时,整个 async 函数就会进入 rejected 状态,并将第一个失败的 Promise 的错误传递给 catch() 方法。
下面是一个使用 Promise.race() 的示例,模拟了一个超时请求的情况:
// javascriptcn.com 代码示例 async function getData() { const promise = new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('Timeout')); }, 5000); }); const timeout = new Promise((resolve, reject) => { setTimeout(() => { reject(new Error('Request is taking too long')); }, 3000); }); try { const result = await Promise.race([promise, timeout]); console.log(result); } catch (error) { console.error(error); } } getData();
在这个示例中,我们定义了一个 async 函数 getData(),在其中创建了一个 Promise,模拟了一个 5 秒钟后返回结果的异步操作。同时,我们还创建了一个超时 Promise,模拟了一个 3 秒钟后请求超时的情况。使用 Promise.race() 方法,我们可以等待这两个 Promise 中的第一个完成,并将结果或错误信息作为返回值。
总结
在本文中,我们介绍了 ES6 中的 Promise 和 ES8 中的 async/await 两种异步编程模式。它们都可以使异步代码更加简洁、易读和易维护。Promise 通过链式调用的方式解决了回调函数嵌套的问题,使得异步代码更加清晰易读;async/await 基于 Promise,使用 async 和 await 关键字,使异步代码更加类似同步代码的形式,使得异步代码更加易于编写和理解。
在实际开发中,我们可以根据具体情况选择使用 Promise 或 async/await。当需要等待多个异步操作完成时,可以使用 Promise.all() 或 async/await;当需要等待多个异步操作中的第一个完成时,可以使用 Promise.race() 或 async/await。无论是哪种方式,都可以使我们的异步代码更加清晰、易读和易维护。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/655d797cd2f5e1655d7bf9e2