盗火的普罗米修斯
曾经我在哪本书上看过这样的介绍,Promise 的英文是从希腊语的直译Prometheus演变过来,Promise也就是代表着先知 未来的意思。在希腊神话中,是最具智慧的神明之一,最早的泰坦巨神后代,名字有“先见之明”(Forethought)的意思。泰坦十二神伊阿佩托斯与名望女神克吕墨涅的儿子。普罗米修斯不仅创造了人类,给人类盗来了火,还教会了他们许多知识和技能。 后来由于给人类带来了火种,他吩咐火神给普罗米修斯最严厉的惩罚。每天还要派一直鹰去啄他的肝脏,夜晚的时候肝脏又会重新长出来,夜以继日的承受折磨。异步函数 Promise
我个人觉得Promise 函数跟 普罗米修斯很像,都是代表着处理未来的事情的先知。既然能处理未来的事情,那就代表着它拥有非常的能力,听我慢慢吹来呀~ 首先来看看官方的吹牛文档解释:
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
你看没错吧,是不是很像一个先知可以处理未来的事情?既然是神仙肯定是咱们凡人管不了的,为啥管不了那?就体现在它三个内部状态上:
对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
看见了吗,敲黑板划重点,神仙已经决定的东西就不能改变。而且一旦new Promise对象就不能取消,讲究~
那如何使用那?下面来看一个简单的例子:
const promise = new Promise (function(resolve, reject) { //这是先知啊,先知
if (/* 异步操作成功 */){
resolve(value); // resolve就是这事儿先知同意了,你就干就完了
} else {
reject(error); // reject就是这事儿先知不同意,白b扯了
}
});
复制代码
注意上面的事情其实有没有发生?没有,先知嘛预知未来的事情,什么时候发生那?当然先知同意的时候也就是说状态变成resolved。只要一resolved 马上就可以then了,划重点 敲黑板 then 就是这玩意。
promise.then(function(value) {
// 妥妥的这个已经办了,下面你想咋地吧?可以连式操作无限then下去
}, function(error) {
// 失败了消停的把错误信息打出来把
console.log("错误信息:"+error)
});
复制代码
那么坑人的玩意来了,请看下面代码,谁先打印?
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
复制代码
上面代码中,Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。那么接下来这段
你要是能直接写出答案,基本Promise就不用看了,根据上面的理论,Promise一旦建立马上就执行,但是咱们说了 resolve 是未来的状态,所以第二行代码先等一等,三行代码也不用看了,肯定是四行代码先执行,然后往下走 第六行执行,回过头来 第三行直接成功了,所以会接下去执行,最后一个不用我说了吧,第二行代码需要走个完成的resolve状态,把1传递给第五行的t参数,所以肯定是最后执行的。概念吹完了,来看势力
妥了,接下来看一个用Promise对象实现的 Ajax 操作的实例:
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});
复制代码
是不是很简单,一点都不难,只是把ajax的操作流程放到了先知的内部,让先知帮你获取各种成功失败的状态!结合实际,我们在工作中可能一次要请求好几个接口的数据,Promise提供了一个更加简单的方法
const p = Promise.all([p1, p2, p3]);
复制代码
很好理解,all 的参数代表这一个可执行的任务队列,只要里面有任务就可以往下执行,也就是咱们访问的多个api集合组成的数组。
// 生成一个Promise对象的数组
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
return getJSON('/post/' + id + ".json"); //ID 作为参数 变成动态请求
});
Promise.all(promises).then(function (posts) {
// ...
}).catch(function(reason){
// ...
});
复制代码
还有一个类似 all的api Promise.race,Promise.race方法的参数与Promise.all方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理
const p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error);
复制代码
上面代码中,如果 5 秒之内fetch方法无法返回结果,变量p的状态就会变为rejected,从而触发catch方法指定的回调函数。
妥妥滴,既然到这里我估计你应该懂了,其实任何技术都不难,都能拆分成若干个简单点。任意简单的点组合起来就又复杂了,正所谓大道至简,相信万事万物都是简单的原理,怀着敬畏的心理去学习,终究会得到自己的收获。