差不多一个月没写博客了,现在来开启新的一年第一篇博客。
今天来说说es6规范的promise属性,始终坚信,技术永无止境,无论做什么,都得从底层做起,基础很重要,一步一步,做大做强是吧,不废话了。
在技术领域,永远明白的一个真理,需求推动发展,都是为了更加方便,所谓存在即合理。
学习技术也有几个问题时刻问自己:这个东西是个啥?用来干啥?怎么用?
promise存在的意义
在javascript的世界中,所有代码都是单线程执行的,由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用到回调函数,异步操作就是说在未来某个时间会触发某个操作,说到异步执行,脑子里马上浮现出ajax,看下面这一段代码:
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status === 200) {
return success(request.responseText);
} else {
return fail(request.status);
}
}
}
可以看到,上面通过判断状态,来执行成功或者失败的操作。对于上面的判断等等操作,我们不好复用,有人说,可以传递失败或者成功的函数作为参数,想法很好。但是首先这样代码不好看,而且对于多个依赖请求,那么是无法实现的。我们都知道jquery其中一个很强大的东西就是链式调用,为什么可以,就是每次调用都返回一个jquery对象。正因为有需求,于是promise出现了, es6对其进行了规范。
揭开promise面纱
promise,英文意思为“承诺”,要理解一个函数,就去看他的英文意思,毕竟外国人搞得嘛。
promise是什么?很简单,javascript不是一切皆对象,promise也是javascript的一个特殊对象,通过构造函数Promise生成。
解决了什么问题?promise给出一个承诺,不管失败还是成功都会给出响应,看一段代码。
function randomNum(resolve, reject) {
var timeOut = Math.random() * 2; // 生成0-2随机数
console.log('set timeout to:' + timeOut + ' seconds');
setTimeout(function () {
if (timeOut < 1) {
console.log('resolve...');
resolve('200 OK');
} else {
console.log('reject...');
reject('timeout in' + timeOut + ' seconds');
}
}, timeOut * 1000);
}
//执行
var p1 = new Promise(randomNum);
console.log(p1);
var p2 = p1.then(function (result) {
console.log('成功:' + result);
});
var p3 = p1.catch(function (reason) {
console.log('失败:' + reason);
});
明确上面几个东西:
resolve, reject是js引擎自带的函数,resolve表示成功执行的函数,reject表示失败执行的函数,我们通过一个定时器,来模拟网络的延迟效果。打印一下p1
由于随机数的关系,这时候走的是失败的情况。发现里面有个状态,有个值。这两个属性,我们是直接操作不了的。状态为“rejected”是因为调用了reject函数,值就是reject里面的参数。一图胜千言
promise对象,根据不同的函数调用,状态和值的变化。默认刚刚形成的promise对象,状态为“pending”,值为“undefined”。
我们通过new Promise构造了promise对象,里面传递一个异步处理函数,一旦构造,就会执行这个异步函数,最后根据情况resolve或者reject函数改变他的状态和值。如下图:
promise本质明白了,说説其中几个注意的点:
- 一旦创建,无法中途取消
- reject返回一个错误,最好用Error对象,或者是继承它的对象
- 可以立即调用resolve或者reject(刚刚例子定时调用模拟耽搁时间),我们经常都是网络请求数据,中间会耽搁一些时间。
- 只会调用一个resolve或者reject,对于后面的都不会执行
- promise可以立即执行,但是js代码是从上到下,会先执行现行代码,遇到这些promise会先加入队列,执行完现行代码才执行promise这些操作
then/catch
.catch(f) 和 .then(null, f), then(undefined, f)类似 ,只是简写,如果只关心错误,那么可只用catch。
promise扩展——串行和并行
- 串行:顾名思义就是一串的样子,链接形式
- 并行:就是一起调用
先来看看串行:
var promise = new Promise(function (resolve, reject) {
setTimeout(() => resolve(1), 1000);
}).then(function (result) {
console.log(result);
return result * 2;
});
看到上面的代码,第一个resolve返回的值为1,这时候其实是一个promise对象,值为1,然后又调用了then方法,result就是1,也就是上个promise的结果,最后返回了2,当然我们还可以继续往下,这就是链式调用。
并行:
看这样一个需求,假设我们现在想等几个promise都返回结果了,然后我们再处理一些操作,改怎么做呢?Promise.all
var pb1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
var pb2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([pb1, pb2]).then(function (results) {
console.log(results); // 获得一个Array: ['P1', 'P2']
});
最后我们可以看到返回了一个数组结果。将pb1和pb2promise作为参数同时传递进去。all,顾名思义也就是所有。
试想一下,如果我虽然同时要几个promise一起执行,但是呢,我只要首先返回结果的,后面的不要怎么弄?Promise.race
和all一样的调用方法,这里就不写了。race英文有竞赛的意思,也就是谁先返回就要谁。这里有人问了,那么返回了,后面的promise还会执行吗?答案是肯定的,还记得上面说的吗?promise一旦创建,就不能取消,当然还是会执行,只是结果没有用了。