说明:看了promise的自己一笔记,本文档主要是记录一些遇到的难点,自己查阅资料理解的过程
参考文档
1、阮一峰es6入门
一、Promise 的含义(摘自阮一峰ES6ru)
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise
对象。
所谓Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise
对象有以下两个特点。
(1)对象的状态不受外界影响。Promise
对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise
这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise
对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise
对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
注意,为了行文方便,本章后面的resolved
统一只指fulfilled
状态,不包含rejected
状态。
有了Promise
对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise
对象提供统一的接口,使得控制异步操作更加容易。
Promise
也有一些缺点。首先,无法取消Promise
,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise
内部抛出的错误,不会反应到外部。第三,当处于pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
如果某些事件不断地反复发生,一般来说,使用 Stream 模式是比部署Promise
更好的选择。
一、基本实例分析
promise的大致结构如下(具体的解释去看姜方鑫 对promise的理解,前面附又链接,是个很好的解释)
/* 用于描述思维的代码 */
function Promise(executor) {
// 共有三种状态:pending(准备)、fulfilled(完成)、rejected(拒绝)
this.PromiseStatus = 'pending';
// 用于存储返回的数据
this.PromiseValue;
// 完成
var resolve = function(reson) {};
// 拒绝
var reject = function(reson) {};
...
// 开始执行承诺
executor(resolve, reject);
}
Promise.prototype.then = function() {};
Promise.prototype.chain = function() {};
Promise.prototype.catch = function() {};
...
例子
var http = {
get: function(url) {
var promise = new Promise(function(resolve, reject) {
$.ajax({
url: url,
method: 'get',
success: function(data) {
resolve(data);
},
error: function(xhr, statusText) {
reject(statusText);
}
});
});
return promise;
}
};
http.get('solve.php').then(function(data) {
return data;
}, function(err) {
return Promise.reject('Sorry, file not Found.');
}).then(function(data) {
document.write(data);
}, function(err) {
document.write(err);
});
基础内容就不在这里说了,大家可以去看阮一峰老师的es6入门,前面附有链接,我这里记录几个注意的地方,结合上面给出的promise的结构(大致)和代码例子我们理一下整个过程。
1、当http调用get()方法时候执行executor(也就是例子里的Promise里的参数函数)。注:实例化Promise的时候就会执行参数里的函数
2、执行executor内容发出了ajax请求,成功返回,且有返回结果的时候,执行resolve(data),resolve把promise的PromiseStatus的值设为fulfilled, PromiseValue的值设为data;同样reject也是类似,只是把PromiseStatus的值设为rejected
3、当PromiseStatus的值被改来不为pending的时候就会出发promise的then函数 ,then方法的参数接受两个方法参数,分别对应fulfilled和rejected的回调,触发条件是Promise.PromiseStatus的值。 注:这里的then也是个异步
4、当调用Promise的then方法的时候,例如 promise.then( fun1(data){ ... return result }, fun2(data){... return result}) 返回的依旧是一个Promise对象,此时新的Promise对象的PromiseStatus的值就是fun1/fun2函数里返回的值 。所以依旧可以再调用then方法,形成链式调用
第4点附加知识:
1)、当fun1和fun2的返执行没有报错和发生异常的时候,返回的Promise对象的的PromiseStatus=fulfilled,所以触发 下 个then的fulfilled的回调,如果要触发rejected的回调,可以在 fun2 里返回 Promise一个Promise对象,并reject();
2)、当resolve(data)和reject(data),当参数data是Promise对象时候,该参数Promise对象将接管剩下的由then构造的回调链,fulfilled回调和rejected回调的 return 一个Promise对象也一样
二、补充
1、Promise原型上有三个重要方法
//Promise.prototype.then 、Promise.prototype.catch 、Promise.prototype.finnally
//Promise.prototype.catch方法是.then(null,rejection)的别名,用于制定发生错误时候的回调函数
//Promise.prototype.finnally则是无论状态如何都会调用的方法
2、Promise.all()
const p = Promise.all([p1, p2, p3]);
// Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例,
//p的状态:fulfilled: 当p1、p2、p3的状态全为fulfilled,所有返回值组成一个数组作为p的值
//rejected: 当p1、p2、p3其中一个为rejected,首先状态为rejected的值将会传给p
3、Promise.race()
const p = Promise.race([p1, p2, p3]);
// Promise.race()也是将多个Promise实例,包装成一个新的Promise实例
// p的状态,fulfilled: 当p1、p2、p3的状态有一个为fulfilled,首先状态为fulfilled的值将会传给p
// rejected: p1、p2、p3的状态全为rejected,所有返回值组成一个数组作为p的值
4、Promise.resolve()、Promise.reject()
//Promise.resolve(): 返回Promise对象,
//
// 参数: 1、Promise对象,直接返回该Promise对象。
//
// 2、参数是一个thenable对象(),将会将该对象变成Promise对象,并立即执行thenable对 象的then()。
//
// 3、不是Promise和thenable对象,或者不是对象,直接放回一个状态为fulfilled的Promise 对象,且将参数作为值传入。
//
// 4、不带参数,直接放回一个状态为fulfilled的Promise对象。
//Promise.reject(): 返回状态为rejected的Promise对象,
//
// 参数: 直接作为返回Promise对象的值
5、Promise.try
问题引入:
1、下面的代码存在一个问题,因为then是异步的,所以要到本轮事件循环的末尾执行,如果Promise里包裹的是异步请求,这个没毛病,但如果不是(有些时候我们都不知道是不是异步请求),同步就被异步执行了
const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
// next
// now
解决方法1,利用async
const f = () => console.log('now');
(async () => f())();
console.log('next');
// now
// next
解决方法2,用立即执行的匿名函数
const f = () => console.log('now');
(
() => new Promise(
resolve => resolve(f())
)
)();
console.log('next');
// now
// next
2、当抛出一些同步错误,比如(数据库连接错误)就只能用try,catch处理了,如下
try {
database.users.get({id: userId}) //dtabase.user.get()返回一个Promise对象
.then(...)
.catch(...)
} catch (e) {
// ...
}
上面个两种情况发生时候,我们都可以用Promise.try(),很优雅的解决问题
Promise.try(database.users.get({id: userId}))
.then(...)
.catch(...)