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();
复制代码
还是在控制台执行: