Promise
promise是es6提供异步编程的一种解决方案,比回调函数和事件更合理.
promise是代表了未来将要发生的事件,用来传递异步操作的信息。语法上说就是promise是一个对象,从它可以获取异步操作的消息。
特点:
promise有三种状态:pending(未待定状态)、fulfilled(成功状态)、rejected(失败状态)。
这三种状态,只有pending-->fulfilled 或者pending-->rejected 这两种改变,且一经改变就不会再变,这就是promise(承诺)的由来。
如果改变已经发生,再对promise对象添加回调函数,会立即取得结果。这就可以链式调用。
所以有了promise对象,就可以将异步操作以同步操作流程表达出来。避免了层层嵌套。且promise提供了统一的api方便调用。
缺点:
1.无法取消,一旦建立就立即执行。
2.如果不设置回调函数,promise内部跑出错误不会反映到外部。
3.处于pending状态时,不知道进展到哪一状态,是刚刚开始还是即将完成.
用法
Promise 对象是由关键字 new 及其构造函数来创建。这个构造函数有一个参数叫“处理器函数”,称之为执行器,这个执行器是立即执行的,该执行器有两个参数,都是函数。一个是resove函数和reject函数。当异步任务完成且返回结果值的时候,调用resove函数,当异步任务失败且返回失败原因的时候,调用reject函数。
const promise = new Promise(function(resolve, reject) { // ... some code if (/* 异步操作成功 */){ resolve(value); } else { reject(error); }});
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value) { // success}, function(error) { // failure});
Promise 新建后就会立即执行
let promise = new Promise(function(resolve, reject) { console.log('Promise'); resolve();});promise.then(function() { console.log('resolved.');});console.log('Hi!');// Promise// Hi!// resolved
const p1 = new Promise(function (resolve, reject) { // ...});const p2 = new Promise(function (resolve, reject) { // ... resolve(p1);})
上面代码中,p1 和 p2 都是 Promise 的实例,但是 p2 的 resolve 方法将 p1 作为参数,这时 p1 的状态就会传递给 p2。如果调用的时候,p1 的状态是 pending,那么 p2 的回调函数就会等待 p1 的状态改变;如果 p1 的状态已经是 fulfilled 或者 rejected,那么 p2 的回调函数将会立刻执行
then方法
Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。
then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的。
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
getJSON("/posts.json").then(function(json) { return json.post;}).then(function(post) { // ...});
采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用
getJSON("/post/1.json").then(function(post) { return getJSON(post.commentURL);}).then(function (comments) { console.log("resolved: ", comments);}, function (err){ console.log("rejected: ", err);});
上面代码中,第一个then方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为resolved,就调用第一个回调函数,如果状态变为rejected,就调用第二个回调函数
基本上,每一个 Promise 都代表了链中另一个异步过程的完成。
catch方法
Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数
getJSON('/posts.json').then(function(posts) { // ...}).catch(function(error) { // 处理 getJSON 和 前一个回调函数运行时发生的错误 console.log('发生错误!', error);});
Promise 对象的错误具有"冒泡"性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个 catch 语句捕获。
getJSON('/post/1.json').then(function(post) { return getJSON(post.commentURL);}).then(function(comments) { // some code}).catch(function(error) { // 处理前面三个Promise产生的错误});
一般来说,不要在then()方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法
一般总是建议,Promise 对象后面要跟catch()方法,这样可以处理 Promise 内部发生的错误。catch()方法返回的还是一个 Promise 对象,因此后面还可以接着调用then()方法。
finally()
finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的
server.listen(port) .then(function () { // ... }) .finally(server.stop);
finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。
finally本质上是then方法的特例。
Promise.all方法,Promise.race方法
var p = Promise.all([p1,p2,p3]);
Promise.all 方法接受一个数组作为参数,p1、p2、p3 都是 Promise 对象的实例。(Promise.all 方法的参数不一定是数组,但是必须具有 iterator 接口,且返回的每个成员都是 Promise 实例。)
表示:等所有的状态改变,只有所有的状态是成功,才返回成功,否则就返回失败。
Promise.race方法 和all类似
表示:第一个状态改变了 ,p的状态就改变。这个 Promise 实例的返回值,就传递给p的回调函数。
Promise.all方法和Promise.race方法的参数,不是Promise实例,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理。
Promise.resolve 方法,Promise.reject 方法
var jsPromise = Promise.resolve($.ajax('/whatever.json'));
上面代码将 jQuery 生成 deferred 对象,转为一个新的 ES6 的 Promise 对象。
如果 Promise.resolve 方法的参数,不是具有 then 方法的对象(又称 thenable 对象),则返回一个新的 Promise 对象,且它的状态为fulfilled。
Promise.resolve('foo')// 等价于new Promise(resolve => resolve('foo'))
const p = Promise.reject('出错了');
// 等同于const p = new Promise((resolve, reject) => reject('出错了'))p.then(null, function (s) { console.log(s)});// 出错了
ps
ES2020 引入了Promise.allSettled()方法,用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做”Settled“,包含了”fulfilled“和”rejected“两种情况。
Promise.allSettled()方法接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled还是rejected),返回的 Promise 对象才会发生状态变更。
const promises = [ fetch('/api-1'), fetch('/api-2'), fetch('/api-3'),];await Promise.allSettled(promises);removeLoadingIndicator();
上面示例中,数组promises包含了三个请求,只有等到这三个请求都结束了(不管请求成功还是失败),removeLoadingIndicator()才会执行。
Promise.any()
只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。
Promise.any()跟Promise.race()方法很像,只有一点不同,就是Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。
const promises = [ fetch('/endpoint-a').then(() => 'a'), fetch('/endpoint-b').then(() => 'b'), fetch('/endpoint-c').then(() => 'c'),];try { const first = await Promise.any(promises); console.log(first);} catch (error) { console.log(error);}
手写Promise
整理逻辑:
·1.promise是一个类,参数是一个立即执行的执行器,执行器有两个函数参数,resove函数和reject函数,该执行器有三个状态,状态改变:pending-->fufilled状态,pending-->reject 状态,且改变完成后不可更改.
then 方法有两个参数 成功回调函数 和失败回调函数, 在then里判断状态,成功就调成功回调函数,失败就调失败回调函数。
//1.完成promise的简单逻辑
// 1.两个函数 被定义成箭头函数 是要this指向 MyPromise
//1.三个状态属性 定义成const 常量 为了复用
//创建一个promise类
const PENDING = 'pending'
//等待 1.设置常量
const FUFILLED = 'fufilled'
//成功 1.设置常量
const REJECTED = 'rejected'
//失败 1.设置常量
class MyPromise {
//1.constructor接受执行器,执行器立即执行 执行器 两个参数,这两个参数都是函数
constructor(executor){
//1.executor(this.resove,this.rejiect)
//内部错误处理
try{
executor(this.resove,this.rejiect)
}catch{
this.reject(error)
}
}
//1.promise里的状态
status = PENDING //默认值
//1.resove函数的参数
value = undefined //成功回调传递的值
//1.reject函数的参数
reason = undefined //失败回调传递的原因
//1.resove函数
resove=(value)=>{
//1.resove 函数逻辑 状态不是等待 向下执行
if(this.status !== PENDING) return;
this.status === FUFILLED ;
this.value = value ;
}
reject=(reason)=>{
//1.reject 函数逻辑 状态不是等待 向下执行
if(this.status !== PENDING) return;
this.status === REJECTED ;
this.reason = reason ;
}
then(successCallBack,failCallback){
//判断状态 成功就调成功回调函数 失败就调失败回调函数
if(this.status === FUFILLED){
successCallBack(this.value)
}else if (this.status === REJECTED){
failCallback(this.reason)
}
}
}
2.完成promise 的then方法的异步状态
then方法:需要注意事项
// 1 then方法里可以同步也可以异步
// 2.then方法的参数是可选的
// 3.then方法可以多次调用 当异步情况 多次调用时 我们应该将每一次的回调函数都存储起来
// 4 then方法链式调用 then参数 回调函数的参数value 是上面一个的返回值
// 5 then方法返回一个promise对象
// 6 上一个then方法的返回值给下一个 then方法回调的值
// 7 then方法不能返回本身这个promise对象 会发生循环调用
//1.完成promise的逻辑
//2.完成promise 的then方法异步状态
const PENDING = 'pending'
const FUFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor){
try{
executor(this.resove,this.rejiect)
}catch{
this.reject(error)
}
}
status = PENDING
value = undefined
reason = undefined
//成功回调函数初始化
successCallBack = undefined
//失败回调函数初始化
failCallback = undefined
resove=(value)=>{
if(this.status !== PENDING) return;
this.status === FUFILLED ;
this.value = value ;
//判断成功回调是否存在,存在就调用
this.successCallBack && this.successCallBack(this.value)
}
reject=(reason)=>{
if(this.status !== PENDING) return;
this.status === REJECTED ;
this.reason = reason ;
//判断成功回调是否存在,存在就调用
this.failCallback && this.failCallback(this.reason)
}
then(successCallBack,failCallback){
//判断状态 成功就调成功回调函数 失败就调失败回调函数
if(this.status === FUFILLED){
successCallBack(this.value)
}else if (this.status === REJECTED){
failCallback(this.reason)
}else{ //异步情况 判断等待状态
//将成功回调和失败回到存储起来
this.successCallBack=successCallBack;
this.failCallback=failCallback;
}
}
}
//1.完成promise的逻辑
//2.完成promise 的then方法异步状态
//3.完成promise 的then方法多次调用
const PENDING = 'pending'
const FUFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor){
try{
executor(this.resove,this.rejiect)
}catch{
this.reject(error)
}
}
status = PENDING
value = undefined
reason = undefined
//成功回调函数初始化 变成数组,可以存储多个函数回调
successCallBack = []
//失败回调函数初始化 变成数组,可以存储多个函数回调
failCallback = []
//变成数组,可以存储多个函数回调
resove=(value)=>{
if(this.status !== PENDING) return;
this.status === FUFILLED ;
this.value = value ;
// this.successCallBack && this.successCallBack(this.value)
//3 多个回调函数是 循环数组并调用 shift数组方法 从前往后执行 shift 执行一个删掉一个 this.successCallback.shift() 这个返回回调函数 直接调用
while (this.successCallback.length)this.successCallback.shift()(this.value)
} reject=(reason)=>{
if(this.status !== PENDING) return;
this.status === REJECTED ;
this.reason = reason ;
//this.failCallback && this.failCallback(this.reason)
//使用while循环数组 多个回调函数是 循环数组并调用
while (this.failCallback.length) this.failCallback.shift()(this.reason)
} then(successCallBack,failCallback){
if(this.status === FUFILLED){
successCallBack(this.value)
}else if (this.status === REJECTED){
failCallback(this.reason)
}else{
//调用数组的push
this.successCallBack.push(successCallBack);
this.failCallback.push(failCallback);
}
}}
//1.完成promise的逻辑
//2.完成promise 的then方法异步状态
//3.完成promise 的then方法多次调用
//4.then方法的链式调用(返回promise) 以及将上一个then方法的返回值 promise对象传递给下一个
//5 then方法参数的可选
const PENDING = 'pending'
const FUFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
constructor(executor){
try{
executor(this.resove,this.rejiect)
}catch{
this.reject(error)
} }
status = PENDING
value = undefined
reason = undefined
successCallBack = []
failCallback = []
// 变成数组,可以存储多个函数回调
resove=(value)=>{
if(this.status !== PENDING) return;
this.status === FUFILLED ;
this.value = value ;
while (this.successCallback.length)this.successCallback.shift()()
}
reject=(reason)=>{
if(this.status !== PENDING) return;
this.status === REJECTED ;
this.reason = reason ;
while (this.failCallback.length) this.failCallback.shift()()
}
then(successCallBack,failCallback){
// 将then方法参数变为可选参数存在就调用 不存在降至传递下去
successCallback = successCallback ? successCallback : value => value
failCallback = failCallback ? failCallback : reason => { throw reason }
//创建一个promise对象
let promise2 = new MyPromise((resolve,reject)=>{
if(this.status === FUFILLED){
setTimeout(()=>{
//将返回值传递给下一个then调用
let x = successCallBack(this.value)
//判断x的值是普通纸还是promise
//如果是普通纸 直接调动resolve
// 如果是promise对象 查看promise对象返回的结果
// 再根据peomise对象返回的结果 决定调用resolve还是调用reject
resolvePromise(promise2,x,resolve,reject)
})
}else if (this.status === REJECTED){
// failCallback(this.reason)//失败回调 传递原因
setTimeout(() => {
let x = failCallback(this.reason)
resolvePromise(promise2, x, resolve, reject)
}, 0)
}else{
//调用数组的push
//this.successCallBack.push(successCallBack);
// this.failCallback.push(failCallback);
this.successCallback.push(() => {
setTimeout(() => {
try{ //捕捉错误
let x = successCallback(this.value)
resolvePromise(promise2, x, resolve, reject)
}catch(e){
reject(e)
}
}, 0)
})
// this.failCallback.push(failCallback)
this.failCallback.push(() => {
setTimeout(() => {
try{ //捕捉错误
let x = failCallback(this.value)
resolvePromise(promise2, x, resolve, reject)
}catch(e){
reject(e)
}
}, 0)
})
}
}
return promise2;
}}
function resolvePromise(promise, x, resolve, reject) {
if (promise === x) {
return reject(new TypeError("Chaining cycle detected for promise #<Promise> "))
}
if (x instanceof MyPromise) {
// x.then(value=>resolve(value),reason=>reject(reason)) 与下等同
x.then(resolve, reject)
} else {
resolve(x)
} }
ps:下篇讲解all方法 resolve方法 finally方法 catch方法的实现