参考链接:
理解 Javascript 中的 Promise
菜鸟教程
https://www.runoob.com/w3cnote/javascript-promise-object.html
0、背景
js是一种同步编程语言,但是由于回调函数,我们可以使它像异步编程语言一样工作
1、概述
- 是异步编程的一种解决方案。
- 从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。
- Promise 对象表示异步操作的最终完成(或失败)及其结果值
2、Promise的状态
Promise 异步操作有三种状态:
- pending(进行中)
- fulfilled(已成功)
- rejected(已失败)。
除了异步操作的结果,任何其他操作都无法改变这个状态。
Promise 对象只有:
- 从 pending 变为 fulfilled
- 从 pending 变为 rejected 的状态改变。
- 只要处于 fulfilled 和 rejected ,状态就不会再变了即 resolved(已定型)。
const p1 = new Promise(function(resolve,reject){ resolve('success1'); resolve('success2'); }); const p2 = new Promise(function(resolve,reject){ resolve('success3'); reject('reject'); }); //只会执行第一个resolve p1.then(function(value){ console.log(value); // success1 }); p2.then(function(value){ console.log(value); // success3 });
状态的缺点
- 无法取消 Promise ,一旦新建它就会立即执行,无法中途取消。
- 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
- 当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
3、Promise的创建
- 构造函数接受一个名为executor 的函数。
- 此执行函数接受两个参数 resolve 和 reject,它们都是函数。
- Promise 通常用于更容易处理异步操作或阻塞代码,其示例包括文件操作,API调用,DB调用,IO调用等。这些异步操作的启动发生在执行函数中。
- 如果异步操作成功,则通过 promise 的创建者调用resolve 函数返回预期结果,同样,如果出现意外错误,则通过调用 reject 函数传递错误具体信息。
同步
//这个promise会立即执行,并不是异步操作,无法检查该promise的初始状态 var keepsHisWord; keepsHisWord = true; //参数resolve和reject都是函数 promise1 = new Promise(function(resolve, reject) { if (keepsHisWord) { //操作成功--resolve resolve("小智承诺坚持分享好的东西给大家"); } else { //操作失败--reject reject("我没有做到!"); } }); console.log(promise1);
创建一个异步的promise----最常见的方法是用setTimeOut
promise2 = new Promise(function(resolve, reject) { //10s后promise会被执行 setTimeout(function() { resolve({ message: "小智承诺坚持分享好的东西给大家", code: "200" }); }, 10 * 1000); }); console.log(promise2);
10s前
10s后
或
PromiseStatus
- 创建 promise 时,PromiseStatus 将处于 pending 状态,并且 PromiseValue 为 undefined
- 直到 promise 被 resolved 或 rejected 为止。
- 当 promise 处于 resolved 或 rejected 的状态时,就称为 settled(已定型)。 所以 promise 通常从 pending 态转换到 settled 状态。
4.then方法
then方法接受两个函数作为参数,第一个参数是promise执行成功时的回调,第二个参数是promise执行失败时的回调,两个函数只会有一个被调用
特点:
- 在js事件队列的当前运行完成之前,回调函数永远不会被调用
const p = new Promise(function(resolve,reject){ resolve('success'); }); p.then(function(value){ console.log(value); }); console.log('first'); // first // success
- 通过.then形式添加的回调函数,不论什么时候,都会被调用
- 可以添加多个回调函数,会按照插入顺序并且独立运行
const p = new Promise(function(resolve,reject){ resolve(1); }).then(function(value){ // 第一个then // 1 console.log(value); return value * 2; }).then(function(value){ // 第二个then // 2 console.log(value); }).then(function(value){ // 第三个then // undefined console.log(value); return Promise.resolve('resolve'); }).then(function(value){ // 第四个then // resolve console.log(value); return Promise.reject('reject'); }).then(function(value){ // 第五个then //reject:reject console.log('resolve:' + value); }, function(err) { console.log('reject:' + err); });
then 方法将返回一个 resolved 或 rejected 状态的 Promise 对象用于链式调用,且 Promise 对象的值就是这个返回值。
-注意点:
- 简便的promise链式编程最好保持扁平化,不要嵌套promise
4、理解promise对象
- Promise 对象具有静态方法和原型方法,
- Promise 对象中的静态方法可以独立应用,而原型方法需要应用于 Promise对象的实例。
- 记住,普通方法和原型都返回一个 promise,这使得理解事物变得容易得多。
1、原型方法Prototype Methods
三种方法:
这些方法都可以应用于promise对象的一个实例,并且所有这些方法依次返回一个promise。
当创建一个 Promise 时,它处于 pending 状态。Promise 根据是否 fulfilled(已成功)或rejected(已失败),将运行以下三种方法中的一种或多种。
Promise.prototype.catch(onRejected) Promise.prototype.then(onFulfilled, onRejected) Promise.prototype.finally(onFinally)
下图显示了 then 和 .catch 方法的流程。
- 由于它们返回一个 Promise ,它们可以再次被链式调用。
- 不管 promise 最后的状态,在执行完then 或 catch 指定的回调函数以后,都会执行finally方法指定的回调函数。
promise.all方法,Promise.race方法
- Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
var p = Promise.all([p1,p2,p3]);
上面代码中,Promise.all 方法接受一个数组作为参数,p1、p2、p3 都是 Promise 对象的实例。(Promise.all 方法的参数不一定是数组,但是必须具有 iterator 接口,且返回的每个成员都是 Promise 实例。)
Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
var p = Promise.all([p1,p2,p3]);
上面代码中,Promise.all 方法接受一个数组作为参数,p1、p2、p3 都是 Promise 对象的实例。(Promise.all 方法的参数不一定是数组,但是必须具有 iterator 接口,且返回的每个成员都是 Promise 实例。)
Promise.race 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
var p = Promise.race([p1,p2,p3]);
上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值。
如果Promise.all方法和Promise.race方法的参数,不是Promise实例,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理。
Promise.resolve 方法,Promise.reject 方法
有时需要将现有对象转为Promise对象,Promise.resolve方法就起到这个作用。
var jsPromise = Promise.resolve($.ajax('/whatever.json'));
上面代码将 jQuery 生成 deferred 对象,转为一个新的 ES6 的 Promise 对象。
如果 Promise.resolve 方法的参数,不是具有 then 方法的对象(又称 thenable 对象),则返回一个新的 Promise 对象,且它的状态为fulfilled。
var p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
上面代码生成一个新的Promise对象的实例p,它的状态为fulfilled,所以回调函数会立即执行,Promise.resolve方法的参数就是回调函数的参数。
如果Promise.resolve方法的参数是一个Promise对象的实例,则会被原封不动地返回。
Promise.reject(reason)方法也会返回一个新的Promise实例,该实例的状态为rejected。Promise.reject方法的参数reason,会被传递给实例的回调函数。
var p = Promise.reject('出错了');
p.then(null, function (s){
console.log(s)
});
// 出错了
上面代码生成一个Promise对象的实例p,状态为rejected,回调函数会立即执行。
使用 promise 的经验法则
- 使用异步或阻塞代码时,请使用 promise。
- 为了代码的可读性,resolve 方法对待 then, reject 对应 catch 。
- 确保同时写入.catch 和 .then 方法来实现所有的 promise。
- 如果在这两种情况下都需要做一些事情,请使用 .finally。
- 我们只有一次改变每个promise (单一原则)。
- 我们可以在一个promise 中添加多个处理程序。
- Promise对象中所有方法的返回类型,无论是静态方法还是原型方法,都是Promise。
- 在Promise.all中,无论哪个promise 首先未完成,promise 的顺序都保持在值变量中。