JavaScript 中的 Promise 和 Async await。
承诺:
想象一下你是一位顶级歌手,粉丝们日夜询问你即将推出的歌曲。
为了减轻他们的负担,你承诺歌曲发布后会发给他们。你给粉丝们一个列表。他们可以填写自己的电子邮件地址,这样当歌曲发布时,所有订阅者都能立即收到。即使发生意外,比如录音室发生火灾,导致你无法发布歌曲,他们仍然会收到通知。
这是我们在编程中经常遇到的一个现实生活中的类比:
- “生产代码”是指执行某些操作并耗费时间的代码。例如,一些通过网络加载数据的代码。这就是“歌手”。
- “消费代码”希望在“生产代码”准备好后立即获得其结果。许多函数可能需要该结果。这些就是“粉丝”。
- promise 是一个特殊的 JavaScript 对象,它将“生产代码”和“消费者代码”连接在一起。打个比方:promise 就是一个“订阅列表”。“生产代码”会花费所需的时间来生成承诺的结果,而“promise”会在结果准备就绪后将其提供给所有订阅代码。
Promises 是 ES6 的一项新功能。它是一种编写异步代码的方法。在处理多个异步操作时,Promises 非常易于管理,而回调可能会造成回调地狱,导致代码难以管理。
工作原理。
Promise对象有3种状态:
1.Pending:初始状态,在 Promise 成功或失败之前
2.Resolved:已完成的 Promise
3.Rejected:失败的 Promise
一步步创建并使用 Promise
首先,我们使用构造函数创建一个 Promise 对象:
const myPromise = new Promise();
它需要两个参数,一个表示成功(解决),一个表示失败(拒绝):
const myPromise = new Promise((resolve, reject) => {
// condition
});
最后,会有一个条件。如果满足条件,则 Promise 被解决,否则被拒绝:
const myPromise = new Promise((resolve, reject) => {
let condition;
if(condition is met) {
resolve('Promise is resolved successfully.');
} else {
reject('Promise is rejected');
}
});
这样我们就创建了第一个 Promise。现在让我们来使用它。
对于已解决的承诺,则使用 then() 方法:
myPromise.then();
在 Promise 被解析后,then() 方法会被调用。然后我们就可以决定如何处理这个被解析的 Promise。
例如,让我们将从 Promise 获得的消息记录到控制台:
myPromise.then((message) => {
console.log(message);
});
catch() 用于已拒绝的 Promise:
然而,then() 方法仅适用于已解析的 Promise。如果 Promise 失败了怎么办?这时,我们需要使用 catch() 方法。
同样,我们附加 then() 方法。我们也可以在 then() 之后直接附加 catch() 方法:
例子,
myPromise.then((message) => {
console.log(message);
}).catch((message) => {
console.log(message);
});
异步函数——让 Promise 更友好
ECMAScript 2017 中添加了 async 函数和 await 关键字。这些功能基本上充当
了 promise 之上的语法糖,使异步代码更易于编写和阅读。
Async 关键字
首先,我们有 async 关键字,将它放在函数声明前面,可以将其转换为异步函数。异步函数是一种知道如何预期 await 关键字调用异步代码的可能性的函数。
它们允许你像编写同步代码一样编写基于 Promise 的代码,但不会阻塞主线程。它们使你的异步代码不那么“聪明”,但可读性更高。
异步函数的工作原理如下:
async function myFirstAsyncFunction() {
try {
const fulfilledValue = await promise;
}
catch (rejectedValue) {
// …
}
}
如果在函数定义前使用 async 关键字,则可以在函数内部使用 await。当您 await 一个 Promise 时,该函数会以非阻塞方式暂停,直到 Promise 完成。如果 Promise 完成,您将获得返回值。如果 Promise 拒绝,则会抛出被拒绝的值。
示例:记录获取操作
假设我们想要获取一个 URL,并将响应以文本形式记录下来。以下是使用 Promise 的示例:
function logFetch(url) {
return fetch(url)
.then(response => response.text())
.then(text => {
console.log(text);
}).catch(err => {
console.error('fetch failed', err);
});
}
使用异步函数可以实现同样的效果:
async function logFetch(url) {
try {
const response = await fetch(url);
console.log(await response.text());
}
catch (err) {
console.log('fetch failed', err);
}
}
代码行数相同,但所有回调都消失了。这使得代码更易于阅读,特别是对于那些不太熟悉 Promise 的人来说。
添加错误处理
如果您想添加错误处理,您有几个选择。
您可以将同步的 try...catch 结构与 async/await 一起使用。此示例扩展了我们上面展示的代码的第一个版本:
async function myFetch() {
try {
let response = await fetch('coffee.jpg');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
let myBlob = await response.blob();
let objectURL = URL.createObjectURL(myBlob);
let image = document.createElement('img');
image.src = objectURL;
document.body.appendChild(image);
} catch(e) {
console.log(e);
}
}
myFetch();
catch() {} 块传递了一个错误对象,我们称之为 e;我们现在可以将其记录到控制台,它将为我们提供一条详细的错误消息,显示在代码中抛出错误的位置。
如果您想使用我们上面展示的第二个(重构)版本的代码,那么您最好继续混合方法并将 .catch() 块链接到 .then() 调用的末尾,如下所示:
async function myFetch() {
let response = await fetch('coffee.jpg');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.blob();
}
myFetch().then((blob) => {
let objectURL = URL.createObjectURL(blob);
let image = document.createElement('img');
image.src = objectURL;
document.body.appendChild(image);
})
.catch((e) =>
console.log(e)
);
这是因为 .catch() 块会捕获异步函数调用和 Promise 链中发生的错误。如果你在这里使用了 try/catch 块,在调用 myFetch() 函数时,仍然可能会遇到未处理的错误。
文章来源:https://dev.to/santan47/promise-async-await-in-javascript-mlb