Promise从 入门 到 精通

为什么需要 promise

Javascript 是⼀⻔单线程语⾔,所以早期我们解决异步的场景时,⼤部分情况都是通过回调函数来进⾏。

例如在浏览器中发送 ajax 请求,就是常⻅的⼀个异步场景,发送请求后,⼀段时间服务端响应之后我们才能拿到结果。如果我们希望在异步结束之后执⾏某个操作,就只能通过回调函数这样的⽅式进⾏操作。

var dynamicFunc = function(cb) {
setTimeout(function() {
cb();
}, 1000);
}
dynamicFunc(function() {console.log(123)});
例如上⾯这个例⼦,这⾥的 dynamicFunc 就是⼀个异步的函数,⾥⾯执⾏的 setTimeout 会在 1s 之后调 ⽤传⼊的 cb 函数。按照上⾯的调⽤⽅式,最终 1s 之后,会打印 123 这个结果。
同样的,如果后续还有内容需要在异步函数结束时输出的话,就需要多个异步函数进⾏嵌套,⾮常不利 于后续的维护。

 

setTimeout(function() {
 console.log(123);
 setTimeout(function() {
 console.log(321);
 // ...
 }, 2000);
}, 1000);
为了能使回调函数以更优雅的⽅式进⾏调⽤,在 ES6 js 产⽣了⼀个名为 promise 的新规范,他让异步操作的变得近乎「同步化」。

Promise 基础

在⽀持 ES6 的⾼级浏览器环境中,我们通过 new Promise() 即可构造⼀个 promise 实例。
这个构造函数接受⼀个函数,分别接受两个参数, resolve reject ,代表着我们需要改变当前实例的状
态到 已完成 或是 已拒绝

 

function promise1() {
 return new Promise(function(resolve, reject) {
 // 定义异步的内容
 setTimeout(function() {
 console.log('1s 后输出');
 // 输出完成后,调⽤函数传⼊的 resolve 函数,将该 promise 实例标记为已完成,当前 promise 串
⾏继续执⾏
 resolve();
 }, 1000);
 }); 
}
function promise2() {
 return new Promise(function(resolve) {
 setTimeout(function() {
 console.log('2s 后输出');
 resolve();
 }, 2000);
 }); 
}
上⾯的两个 promise 实例,串联起来即可写为:
promise1().then(function() { return promise2(); }); 也可以简写为 promise1().then(promise2);
浏览器中执⾏之后,即可看到, 1s 之后出现 1s 后输出 字样,再经过 2s 出现 2s 后输出 字样。在这个例⼦中我们能看到。当前 promise 如果状态变为已完成(执⾏了 resolve ⽅法),那么就会去执⾏ then ⽅法中的下⼀个 promise 函数。
同样的,如果我们的 promise 变为已拒绝状态(执⾏了 reject ⽅法),那么就会进⼊后续的异常处理函数中。
function promise3() {
 return new Promise(function(resolve, reject) {
 var random = Math.random() * 10; // 随机⼀个 1 - 10 的数字
 setTimeout(function() {
 if (random >= 5) {
 resolve(random);
 } else {
 reject(random);
 } 
 }, 1000);
 });
}
var onResolve = function(val) {
 console.log('已完成:输出的数字是', val);
};
var onReject = function(val) {
 console.log('已拒绝:输出的数字是', val);
}

// promise 的 then 也可以接受两个函数,第⼀个参数为 resolve 后执⾏,第⼆个函数为 reject 后执⾏
promise3().then(onResolve, onReject);
// 也可以通过 .catch ⽅法拦截状态变为已拒绝时的 promise
promise3().catch(onReject).then(onResolve);
// 也可以通过 try catch 进⾏拦截状态变为已拒绝的 promise
try {
 promise3().then(onResolve);
} catch (e) {
 onReject(e);
}

这个例⼦使⽤了三种⽅式拦截最终变为「已拒绝」状态的 promise ,分别是 使⽤ then 的第⼆个参数,使 .catch ⽅法捕获前⽅ promise 抛出的异常,使⽤ try catch 拦截代码块中 promise 抛出的异常
同时我们还可以发现,在改变 promise 状态时调⽤ resolve reject 函数的时候,也可以给下⼀步 then中执⾏的函数传递参数。这个例⼦中我们把随机⽣成的数字传给了 resolve reject 函数,我们也就能在then 中执⾏函数的时候拿到这个值。
总结⼀下本⼩节的内容:
  1. promise 会有三种状态,「进⾏中」「已完成」和「已拒绝」,进⾏中状态可以更改为已完成或已拒绝,已经更改过状态后⽆法继续更改(例如从已完成改为已拒绝)。
  2. ES6 中的 Promise 构造函数,我们构造之后需要传⼊⼀个函数,他接受两个函数参数,执⾏第⼀个参数之后就会改变当前 promise 为「已完成」状态,执⾏第⼆个参数之后就会变为「已拒绝」状态。
  3. 通过 .then ⽅法,即可在上⼀个 promise 达到已完成时继续执⾏下⼀个函数或 promise。同时通过resolve reject 时传⼊参数,即可给下⼀个函数或 promise 传⼊初始值。
  4. 已拒绝的 promise ,后续可以通过 .catch ⽅法或是 .then ⽅法的第⼆个参数或是 try catch 进⾏捕 获。

如何封装异步操作为 promise

我们可以将任何接受回调的函数封装为⼀个 promise ,下⾯举⼏个简单的例⼦来说明。
// 原函数
function dynamicFunc(cb) {
 setTimeout(function() {
 console.log('1s 后显示');
 cb();
 }, 1000);
}
var callback = function() {
 console.log('在异步结束后 log');
}
// ⽤传⼊回调函数的⽅式执⾏
dynamicFunc(callback);
上⾯的例⼦就是最传统的,使⽤传⼊回调函数的⽅式在异步结束后执⾏函数。我们可以通过封装
promise 的⽅式,将这个异步函数变为 promise
function dynamicFuncAsync() {
 return new Promise(function(resolve) {
 setTimeout(function() {
 console.log('1s 后显示');
 resolve();
 });
 });
}
var callback = function() {
 console.log('在异步结束后 log');
}
dynamicFuncAsync().then(function() { callback(); });
再举⼀个例⼦,发送 ajax 请求也可以进⾏封装:
function ajax(url, success, fail) {
 var client = new XMLHttpRequest();
 client.open("GET", url);
 client.onreadystatechange = function() {
 if (this.readyState !== 4) {
 return;
 }
 if (this.status === 200) {
 success(this.response);
 } else {
 fail(new Error(this.statusText));
 }
 };
 client.send();
};
ajax('/ajax.json', function() {console.log('成功')}, function() {console.log('失败')});
我们可以看到,调⽤ ajax ⽅法时需要传⼊ success fail 的回调函数进⾏调⽤。我们可以不传⼊回调函数,通过封装 promise 的⽅式,在原来的执⾏回调函数的地⽅更改当前 promise 的状态,就可以通过链式调⽤。
function ajaxAsync(url) {
 return new Promise(function(resolve, reject){
 var client = new XMLHttpRequest();
 client.open("GET", url);
 client.onreadystatechange = function() {
 if (this.readyState !== 4) {
 return;
 }
 if (this.status === 200) {
 resolve(this.response);
 } else {
 reject(new Error(this.statusText));
 }
 };
 client.send();
 });
};
ajax('/ajax.json')
 .catch(function() {
 console.log('失败');
 })
 .then(function() {
 console.log('成功');
 })
总结⼀下当前⼩节:
  1. 我们可以轻松的把任何⼀个函数或者是异步函数改为 promise,尤其是异步函数,改为 promise 之后即可进⾏链式调⽤,增强可读性。
  2. 将带有回调函数的异步改为 promise 也很简单,只需要在内部实例化 promise 之后,在原来执⾏回调函数的地⽅执⾏对应的更改 promise 状态的函数即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值