1. 前言
本文将介绍JavaScript中异步编程技术,包括promise, sync, await的使用。
2. Promise
2.1 简介
Promise 是 JavaScript 中用于处理异步操作的一种对象。它代表了一个异步操作的最终完成(或失败)及其结果值。
Promise 对象有三种状态:
- Pending(进行中)
- Fulfilled(已成功)
- Rejected(已失败)
Promise 的状态一旦改变,就不会再变,无论是从 Pending 变为 Fulfilled,还是从 Pending 变为 Rejected。
2.2 基本用法
Promise 的基本用法如下:
let oMyPromise = new Promise(function(resolve, reject) {
// 异步操作代码
// xxx
// 异步操作在某些条件下成功,某些条件下出错
if (1 === 1) {
resolve(value); // value 是异步操作返回的结果
} else {
reject(error); // error 是异步操作出错时的错误对象
}
});
oMyPromise.then(function(value) {
// 异步操作成功时执行的代码
}, function(error) {
// 异步操作失败时执行的代码
});
Promise实例的构建:
- Promise的构造函数接受一个函数作为参数, 调用构造函数得到实例oMyPromise的同时,作为参数的函数会立即执行。
- 参数函数接受两个回调函数参数resolve和reject,在参数函数被执行的过程中,如果在其内部调用resolve,会将oMyPromise的状态变成fulfilled,或者调用reject,会将oMyPromise的状态变成rejected。
调用.then:
- 调用.then可以为实例oMyPromise注册两种状态回调函数
- 当实例oMyPromise的状态为fulfilled,会触发第一个函数执行
- 当实例oMyPromise的状态为rejected,则触发第二个函数执行
2.3 代码示例
下面是一个具体的 Promise 代码示例:
let oMyPromise = new Promise(function (resolve, reject) {
// 模拟异步操作
setTimeout(function () {
if (Math.random() < 0.5) { // 50% 的概率异步操作成功
resolve('Success!');
} else {
reject(new Error('Failure!'));
}
}, 1000);
});
oMyPromise.then(function (value) {
console.log(value); // 输出 "Success!"
}, function (error) {
console.error(error); // 输出 Error: Failure!
});
在这个示例中,我们创建了一个新的 oMyPromise 对象,然后在 oMyPromise 的执行函数中模拟了一个异步操作。如果异步操作成功(即 Math.random() < 0.5),我们调用 resolve 函数并传入结果 ‘Success!’;如果异步操作失败,我们调用 reject 函数并传入一个新的 Error 对象。然后,我们使用 then 方法来指定异步操作成功和失败时要执行的回调函数。
2.4 链式Promise
Promise支持链式调用,若then()中的函数未返回promise参数,其会自动将其包装成一个promise。
通过链式promise,可以避免回调函数地狱(callback hell),增加异步代码的可读性。
function callBackend(oBackend) {
return oBackend.call(); //返回一个promise
}
function processData(result) {
return result.data; //返回的不是promise
}
oModel.initialize() //返回一个promise
.then(callBackend)
.then(processData) //返回一个包装在promise中的值
.then(data => console.log(data));
3. async和await
链式的promise编程风格虽然较回调函数有了更好的可读性,但这种编程方式仍然是复杂和难懂的。为了进一步简化异步编程,JavaScript中提供了关键字async和await。
async和await是用于处理异步操作的特殊关键字。它们可以让你以同步的方式编写异步代码,使得代码更易于阅读和理解。
// 传统的promise风格异步代码
function callBackend(oBackend) {
let data1, data2;
return oBackend.call()
.then(data => { data1 = data })
.then(oBackend.call())
.then(data => { data2 = data })
.then(() => process(data1, data2));
}
//async & await 风格
async function callBackend(oBackend) {
let data1 = await oBackend.call();
let data2 = await oBackend.call();
return process(data1, data2);
}
3.1 async
async关键字用于声明一个函数是异步的,这意味着函数总是返回一个Promise。如果一个async函数返回一个值,JavaScript会自动将其包装为Promise.resolve()。如果函数抛出异常,则会被Promise.reject()捕获。
async function myAsyncFun() {
return 'Hello, World!';
}
myAsyncFun().then(console.log); // 输出: 'Hello, World!'
3.2 await
await关键字只能在async函数内部使用。它会暂停代码的执行,直到Promise被解析,然后返回结果值。如果Promise被拒绝,await将抛出异常。
async function myAsyncFun() {
let response = await fetch('https://api.github.com/users/github');
let user = await response.json();
return user.name;
}
myAsyncFun().then(console.log); // 输出: 'GitHub'
在上面的例子中,fetch函数返回一个Promise,这个Promise解析为服务器的响应。然后,我们使用await关键字等待Promise解析,然后将结果赋值给response变量。然后,我们再次使用await关键字等待response.json()的Promise解析,然后将结果赋值给user变量。
注意,如果你在没有async函数的上下文中使用await,JavaScript会抛出一个SyntaxError。
3.3 异常处理
由于await会抛出被拒绝的Promise的异常,因此你需要使用try/catch语句来处理可能的错误。
async function myAsyncFun() {
try {
let response = await fetch('https://api.github.com/users/github');
let user = await response.json();
return user.name;
} catch (error) {
console.error('Error:', error);
}
}
myAsyncFun().then(console.log); // 如果请求成功,输出: 'GitHub'
在上面的例子中,如果fetch或response.json()的Promise被拒绝,catch块将捕获异常,并打印错误信息。
3.4 真实例子
一个常见的使用async和await的场景是在Web开发中从服务器获取数据。下面是一个示例,它从JSONPlaceholder API获取帖子,并打印出帖子的标题。
async function getPosts() {
try {
// 发送GET请求到API
let response = await fetch('https://jsonplaceholder.typicode.com/posts');
// 如果响应状态不是200,抛出错误
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// 解析响应数据为JSON
let posts = await response.json();
// 打印每个帖子的标题
for (let post of posts) {
console.log(post.title);
}
} catch (error) {
console.log('There was a problem with the fetch operation: ' + error.message);
}
}
// 调用函数
getPosts();
在这个例子中,我们首先使用fetch函数发送一个GET请求到API。然后,我们检查响应的状态码是否为200(表示请求成功)。如果状态码不是200,我们抛出一个错误。然后,我们使用response.json()方法解析响应数据为JSON格式。最后,我们遍历帖子数组,打印出每个帖子的标题。
注意,我们使用try/catch语句来处理可能的错误。如果在fetch操作中发生错误(例如,网络问题或API返回非200状态码),我们将捕获错误并打印错误消息。
4. 小结
本文介绍了JavaScript中异步编程技术,包括promise, sync, await的使用,并给出了异步编程的一个真实例子。希望本文对你有帮助!