Promise是什么?
通俗来讲,Promise就是一个用于保存异步操作的容器。当一个异步操作完成后,它要么保存了值,要么保保存了错误的信息。Promise基本上就是“承诺”你它会给你一个异步操作的结果。
这个对象分为三个阶段:
pending阶段,创建promise对象时,处于挂起状态,关联某个异步操作。Fullfilled阶段,表示promise有结果将被履行,异步操作成功完成,有结果了。Rejected阶段,表示异步操作过程中出现问题,被拒绝了,得到错误值。
首先创建一个Promise对象,这个构造函数需要一个参数,这个参数是一个需要两个参数的函数。分别是resolve和reject两个参数的匿名函数
const p = new Promise((resolve,reject) => {
//成功后执行的函数
resolve('hello')
//失败后执行的函数
reject(new Error('message'))
})
我们平常查询一个数据库,请求一个网络服务或者设置一个延时执行的程序都会用到异步操作,而promise对象也就非常适合这些场合使用。
因为使用了promise,所以后面一定会给我们一个异步操作的结果,不论是成功的函数失败的。所以我们需要将值返回给promise的使用者,我们就用上面的resolve和reject参数来实现。
假设上面的异步操作完成了,执行的结果就是“hello”。在现实编程中,这个“hello”也可以是一个从数据库中读取的信息,这就是异步操作的结果。现在我们需要兑现了,因为后续可能使用了p对象。看它有两个方法。
catch用于获取任何的报错,then用来获取异步操作成功的返回值,我们调用then,然后传入一个函数。这里我们传入result,就是里面的resolve函数。
const p = new Promise((resolve, reject) => {
//成功后执行的函数
resolve("hello");
//失败后执行的函数
reject(new Error("message"));
});
p.then(result => console.log("result", result));
打开控制台,运行promise.js。
hello,很好,再次改造下添加一个定时器,实现异步操作。
const p = new Promise((resolve, reject) => {
//成功后执行的函数
setTimeout(() => resolve("hello"), 2000);
//失败后执行的函数
// reject(new Error("message"));
});
p.then(result => console.log("result", result));
再次运行,可以看到间隔2秒后返回了结果。
error返回给调用者,最后链接上catch,当遇到什么问题的时候可以了解到具体的错误是什么。
const p = new Promise((resolve, reject) => {
//成功后执行的函数
setTimeout(() => {
// resolve("hello");
reject(new Error("message"));
}, 2000);
});
p.then(result => console.log("result", result)).catch(err =>
console.log("Error", err.message)
);
现在总结一下,Promise是一个对象,用于保存异步操作的任何结果,当我们创建对象的时候进入了挂起的状态。上面的代码就是它开始处理一个异步操作,这个操作有可能成功也可能失败。
如果成功了,我们就说承诺“解决”了,或者是被履行了。Promise的状态由挂起转为解决或者履行,我们就用resolve函数返回结果给调用者。
如果异步操作失败了,Promise的状态由挂起转为拒绝,我们就用reject函数返回一个错误给调用者。
这就是如何创建一个Promise,然后是如何使用它。我们使用then来得到结果,catch来捕捉错误对象。有一点需要提到的是如果在任何地方用到了异步的回调,都应该让函数返回一个Promise。
Promise并行操作是什么?
有时我们可能想要并行的处理几个异步请求,当它们全部结束后再做点什么。比如可能同时请求两个或以上网站的API,当所有的请求都完毕的时候就可以返回什么给客户端了。
首先创建两个Promise对象,这里我们需要返回结果,不要被拒绝,所以就一个参数。
const p1 = new Promise(resolve => {
setTimeout(() => {
console.log("异步操作1");
resolve(1);
}, 2000);
});
const p2 = new Promise(resolve => {
setTimeout(() => {
console.log("异步操作2");
resolve(2);
}, 2000);
});
现在我想同时处理这两个请求,当它们都完成的时候我们再做些什么。这里调用Promise.all(),这是另一个Promise类的静态方法,而不是Promise对象的实例方法。然后传入一个Promise对象的数组,这里是p1,p2,将会返回一个新的Promise。它的履行取决于所有包含Promise对象全部履行。先调用这个Promise,然后调用then,将结果显示在控制台。
Promise.all([p1, p2]).then(result => console.log(result));
如果其中一个Promise对象失败了呢?我们修改第一个Promise,让它有reject参数
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("异步操作1");
reject(new Error("出现错误了"));
}, 2000);
});
const p2 = new Promise(resolve => {
setTimeout(() => {
console.log("异步操作2");
resolve(2);
}, 2000);
});
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(err => console.log("Error", err.message));
再次运行查看,只要其中一个Promise出错了,全部Promise的最终返回值都会被拒绝。
Promise实现了就立即进行某些操作,而不是等到所有全部履行。这样的话可以使用race方法取代all。
const p1 = new Promise(resolve => {
setTimeout(() => {
console.log("异步操作1");
resolve(1);
}, 2000);
});
const p2 = new Promise(resolve => {
setTimeout(() => {
console.log("异步操作2");
resolve(2);
}, 2000);
});
Promise.race([p1, p2])
.then(result => console.log(result))
.catch(err => console.log("Error", err.message));
Promise履行,就可以拿到结果。
Async/Await是什么?
在现在的JavaScript中有个新特性,async和await,这可以让你写同步代码一样写异步代码。
function getUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("读取id中...");
resolve({
id: id,
gitHubUsername: "mosh"
});
}, 2000);
});
}
function getRepositories(username) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("获取用户信息中...");
resolve(["repo1", "repo2", "repo3"]);
}, 2000);
});
}
function getCommits(text) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("获取内容中...");
resolve(["hello"]);
}, 2000);
});
}
要注意的是使用Async/Await,在Promise中我们使用try-catch块来捕捉异常,它没有catch方法。在这个函数中,我们将所有的异步操作代码包含到try块中,然后是catch块。捕捉到的是err对象。如果有异常,catch块的代码就执行。
async function displayCommits() {
try {
const user = await getUser(1);
const repos = await getRepositories(user.gitHubUsername);
const commits = await getCommits(repos[0]);
console.log(commits);
} catch (err) {
console.log("Error", err.message);
}
}
displayCommits();
还是在控制台执行: