Promise 基础使用
为什么要使用Promise
JS 是一门单线程单线程的语言,所以在早期解决异步的场景时,大部分情况下是使用回调函数的形式。
例如我们在浏览器中发送一个ajax请求。发送请求之后要过一段时间,浏览器响应之后才会返回给我们结果,如果我们希望在异步请求之后进行某些操作,那么只能通过回调函数来进行。
var syncFunc = function(cb) { setTimeout(function() { cb(); }, 1000); } syncFunc(function() {console.log(1234)}); $.ajax(url, method, function() {});
像上面的例子,syncName 就是一个异步函数,里面的setTimeout会在1s 之后执行传入的回调函数 cb 。按照上面的方式,在1s之后会打印123。
同样,如果后续还有内容需要在异步函数结束之后操作的话,就需要多个异步函数嵌套,非常不利于维护和阅读。
setTimeout(function() { console.log(111); setTimeout(function() { console.log(222); // ... }, 1000); }, 1000);
为了能使回调函数以更加优雅的方式进行调用,在ES6中就产生了一个 Promise 的新的规范,他让异步操作变得几乎像是同步的操作。
Promise 的使用
在支持ES6的现代浏览器环境中,我们可以直接使用 new Promise()
就可以创建一个Promise
实例。
这个构造函数接受一个函数,分别接受两个参数。resolve
和 reject
, 表示我们需要改变当前实例的转态到 已完成
或者 已拒绝
。
function promise1() { return new Promise(function(resolve, reject) { // 定义异步 setTimeout(function() { console.log('promise1'); // 最后调用传入的 resolve 函数,将该Promise 实例标记为已完成 resolve(); }, 1000); }); } function promise2() { return new Promise(function(resolve, reject) { setTimeout(function() { console.log('promise2'); resolve(); }, 1000); }); }
上面两个Promise 实例直接链式调用就可以按顺序执行:
promise1().then(function() {return promise2();}); // 或者简写成 promise1().then(promise2);
上面的代码在浏览器中执行后,就能看到在1s后输出promise1
,再过1s后输出promise2
。从这个demo我们可以看到,当前Promise如果转态变为已完成,即执行了resolve方法治好,就会立即去执行then
方法中的下一个Promise实例。
如果我们的Promise实例转态变为了已拒绝,即执行了reject
方法,那么就会进入后续的异常处理函数中。
function promise3() { return new Promise(function(resolve, reject) { var random = Math.random() * 10; 随机生成一个0-10 的随机数 setTimeout(function() { if (random > 5) { reject(random); } else { resolve(random); } }, 1000); }); } var onResolve = function(val) { console.log('resolve input::', val); } var onReject = function(val) { console.log('reject input::', val); } // Promise的.then 方法也可以接收两个函数,第一个函数作为`resolve`后执行,第二个函数作为`reject`后执行 promise3().then(onResolve, onReject); // 也可以通过 .catch 拦截变为已拒绝的Promise promise3() .catch(onReject) .then(onResolve); // 也可以通过 try catch 进行拦截变为已拒绝的 Promise try { promise3.then(onReject); } catch(e) { onReject(e); }
这个例子使用了三种方式来拦截Promise变为已拒绝
的状态,分别是then的第二个参数,.catch方法捕获抛出的异常,try catch 拦截代码块中的错误。
我们在改变Promise状态是调用的resolev 和reject 函数的时候,也可以向下一步的then或者catch中传递参数。
同时,我们好可以发现,我们早改变Promise转态的时候,还可以给下一步then或者catch中传递参数。
总结一下:
-
Promise有三种转态,
进行中
、已完成
、已拒绝
,进行中的转态可以更改为已完成或者已拒绝,已经更改过的转态无法继续更改。 -
ES6中的Promise构造函数,我们构造后需要传入一个函数,这个函数接受两个参数,即
resolve
和reject
,执行resolve后,当前Promise变为已完成转态,执行reject之后,当前Promise变为已决绝转态。 -
通过
.then
方法,即可在上一个Promise达到已完成时继续执行下一个函数或者下一个Promise,同时可以通过resolve或者reject传入参数,给下一个函数或者Promise传入初始值。 -
已拒绝的Promise,可以通过
.catch
或者.then
的第二个参数或者try catch
进行捕获。
封装异步操作为Promise
我们可以将任何接受回调的函数封装成一个Promise,例如封装第一个函数:
// 原函数 function syncFunc(cb) { setTimeout(function() { cb(); }, 1000); } var callback = function() { console.log('success'); } syncFunc(callback); // 封装之后 function syncFunc() { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(); }, 1000); }); } var callback = function() { console.log('success'); } syncFunc().then(callback);
小结:
- 我们可以轻松的吧任何一个函数或者异步的函数改为Promise,尤其是异步函数,改为Promise之后可以进行链式调用。
- 将带有回调函数的异步改为Promise也很简单,只需要在内部实例化Promise之后,在原理执行回调的地方执行对应更改Promise转态的函数即可。