介绍
在本教程的第一部分中,我们学习了异步编程和使用回调的原理。 回顾一下,异步编程允许我们通过稍后执行阻塞任务来编写非阻塞代码。 回调函数提供了一种同步代码执行的方法。
但是,重复嵌套回调不是遵循的好模式。 来救人的诺言来了。 在JavaScript库中使用Promise已有一段时间,但是现在您可以在代码中本地使用它们。 异步功能允许我们一个接一个地编写任务,而不必担心执行时间,从而改善了Promise。
内容
- 承诺
- 异步功能
- 评论
- 资源资源
承诺
让我们看一下诺言在概念上是什么。 想象一下您在线购物并购买一双鞋的情况。 结帐时,会通过电子邮件向您发送购买摘要。
此订单确认就像一个承诺。 诺言是您的保证,以后您会从公司得到一些回报。 当然,在等待订单期间,您的生活不会停止。 您将继续执行其他任务,例如浏览互联网。 如果您的订单得到满足,您将收到一封包含运输信息的电子邮件。 您的订单有可能被拒绝。 您订购的商品可能缺货,或者付款方式可能有问题。 在这种情况下,您会收到一封电子邮件,告诉您有关错误的信息。
用代码来说,promise是一个对象,可确保无论请求成功还是失败,我们都会为我们的请求获得未来的价值。 这是创建和使用承诺的一般形式:
function task1() {
return new Promise(function(resolve, reject) {
resolve(data);
reject(error);
});
}
task1()
.then(function(result){
console.log(result);
})
.catch(function(error){
console.log(error);
});
要创建一个Promise,您需要实例化一个Promise对象,并在Promise的回调函数中编写异步代码。 您希望从诺言中返回的数据将作为参数传递给resolve
函数,而您的错误消息将传递给reject
函数。 我们使用then
方法将诺言链接在一起。 这使我们可以按顺序执行任务。
如果需要将任务的结果传递给下一个任务,则可以在then
方法中将其返回。 当我们有兴趣转换值或需要按特定顺序执行代码时,我们可能希望将承诺链接在一起。 在链的末端,我们发现了我们的错误。 如果在我们的任何任务中发生错误,那么剩余的任务将被跳过,并将错误发送到我们的catch块。
在本教程的第一部分中,我们使用回调打开文件并检索帖子及其评论。 这是使用promise的完整代码:
index.js
const fs = require('fs');
const path = require('path');
const postsUrl = path.join(__dirname, 'db/posts.json');
const commentsUrl = path.join(__dirname, 'db/comments.json');
//return the data from our file
function loadCollection(url) {
return new Promise(function(resolve, reject) {
fs.readFile(url, 'utf8', function(error, data) {
if (error) {
reject('error');
} else {
resolve(JSON.parse(data));
}
});
});
}
//return an object by id
function getRecord(collection, id) {
return new Promise(function(resolve, reject) {
const data = collection.find(function(element){
return element.id == id;
});
resolve(data);
});
}
//return an array of comments for a post
function getCommentsByPost(comments, postId) {
return comments.filter(function(comment){
return comment.postId == postId;
});
}
//initialization code
loadCollection(postsUrl)
.then(function(posts){
return getRecord(posts, "001");
})
.then(function(post){
console.log(post);
return loadCollection(commentsUrl);
})
.then(function(comments){
const postComments = getCommentsByPost(comments, "001");
console.log(postComments);
})
.catch(function(error){
console.log(error);
});
此处的区别在于,我们打开文件的方法现在包装在promise对象中。 而不是与回调嵌套我们的任务,他们正在与链接在一起then
。
如您所见,我们尚未消除对回调的需求。 我们只是以不同的方式使用它们。 之前,我们嵌套了回调,以便我们可以在下一个任务中继续执行代码。
这使我想起了当我致电客户服务部就问题而不是由代理商解决我的问题时,我被转移到其他人那里。 别人可能会也可能不会解决该呼叫,但是就第一座席而言,这是别人的责任。
有了承诺,我们将在进行下一个任务之前得到一些回报。 如果我们需要将这些内容带入代码的下一个延续,则可以使用then
语句。
任务
使用promise,编写一个程序,该程序将打开用户文件,获取用户信息,然后打开帖子文件并打印用户的所有帖子。
异步功能
承诺是对程序设计的一种改进,但我们可以做得更好。 如果我们可以像这样同步执行任务,将非常方便:
task1();
task2();
task3();
好吧,我们可以使用异步/等待模式。 为此,我们首先将任务包装在异步函数中。 该函数返回一个promise。 然后,我们通过将任务包装在try/catch
语句中来实现错误处理。
如果实现了承诺,它将完成try
块内的所有任务。 如果拒绝,将执行catch
块。 在任何任务暂停我们的程序之前添加await
关键字,直到任务完成。
这是使用异步函数的一般形式:
async function initTasks () {
try {
const a = await task1();
const b = await task2();
const c = await task3();
//do something with a, b, and c
} catch (error) {
//do something with the error object
}
}
initTasks();
使用此模式,我们可以在文件示例中重写如何执行代码。
async function getPost(){
try {
const posts = await loadCollection(postsUrl);
const post = await getRecord(posts, "001");
const comments = await loadCollection(commentsUrl);
const postComments = await getCommentsByPost(comments, post.id);
console.log(post);
console.log(postComments);
} catch (error) {
console.log(error);
}
}
getPost();
我喜欢用try/catch
语句构造异步代码,因为它可以将错误处理代码与常规代码清晰地分开。 如果try
块中的任何代码导致错误,它将由catch
块处理。 另外,我们可以添加一个finally
块,无论我们的任务成功与否,该代码都将执行代码。
使用此模式的一个示例是当我们有需要执行的清理代码时。 此代码不一定必须包含在finally
块中。 可以在catch
语句之后编写它,然后执行。 承诺没有内置此语法。我们必须在catch
语句之后链接另一个then
语句,以实现相同的效果。
任务
使用async / await,编写一个程序,该程序将打开用户文件,获取用户信息,然后打开帖子文件并打印用户信息及其所有帖子。
评论
回调并不是天生的邪恶。 在JavaScript中,将函数传递给其他函数是一种有用的模式。 当我们使用回调来控制应用程序逻辑流时,回调便成为问题。 由于JavaScript是异步的,因此我们必须注意如何编写代码,因为任务不一定按其编写顺序完成。 这不是一件坏事,因为我们不希望任何任务阻碍程序的继续。
承诺是编写异步代码的更好模式。 它们不仅可以解决嵌套回调的混乱问题,而且还可以控制任务中任务的结果。 将控制权传递给另一个任务,无论该任务是我们自己的代码还是第三方API,都会使我们的代码可靠性降低。 最后,异步函数使我们能够同步编写代码,这更加直观和易于理解。