es6之Promise是什么「建议收藏」

es6之Promise是什么「建议收藏」Promise的含义Promise是一个容器,内部保存着某个未来才会结束的事件(通常是一个异步操作)的结果。Promise也是一个对象,可以通过这个对象获取异步操作的消息。Promise的特点:对象的状态不受外部影响,只有异步操作的结果才能决定状态。一共有三种状态:pending(进行中)、fulfilled(成功的)和rejected(失败的)。对象的状态发生改变后,不会再变化,并且随时可以得到这个结果。对象的状态改变只有两种情况:pending=》fulfilled,pending=》reje

大家好,又见面了,我是你们的朋友全栈君。

Promise的含义

Promise是一个容器,内部保存着某个未来才会结束的事件(通常是一个异步操作)的结果。Promise也是一个对象,可以通过这个对象获取异步操作的消息。
Promise的特点:

  1. 对象的状态不受外部影响,只有异步操作的结果才能决定状态。一共有三种状态:pending(进行中)、fulfilled(成功的)和rejected(失败的)。
  2. 对象的状态发生改变后,不会再变化,并且随时可以得到这个结果。对象的状态改变只有两种情况:pending=》fulfilledpending=》rejected

Promise的缺点:

  1. 无法取消Promise,一旦创建就会立即执行
  2. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  3. 当对象状态是pending时,无法得知当前进行到哪一步(刚刚开始还是即将完成)。

基本用法

  1. 创建Promise实例
const promise = new Promise(function(resolve, reject){ 
   
	// ...some code
	if (/*异步操作成功*/) { 
   
		resolve(value);
	} else { 
   
		reject(error);
	}
});

Promise构造函数接收一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供,不用自己部署。
resolve函数的作用:将Promise对象状态从“未完成”变为“成功”(pending=》resolved)。在异步操作成功时调用,并将异步操作的结果作为参数传递出去。
reject函数的作用:将Promise对象状态从“未完成”变为“失败”(pending=》rejected)。在异步操作失败时调用,将异步操作爆出的错误,作为参数传递出去。

  1. 调用Promise
    Promise实例生成后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value){ 
   
	//success Promise对象的状态变为resolved时调用
}, function(error){ 
   
	//failure Promise对象的状态变为rejected时调用,该函数为可选参数
});
  1. reject函数和resolve函数参数
    reject函数的参数通常是Error对象的实例。
    resolve函数的参数除了正常的值意外,还可能是另一个Promise实例。

Promise.prototype.then()

Promise实例具有then方法,即then方法是定义在原型对象Promise.prototype上的。它的作用是为Promise实例添加状态改变时的回调函数。then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。
then方法返回的是一个新的Promise实例(不是原来的那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

// 第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数,回调函数可以采用箭头函数形式,更加简洁
getJSON("/posts.json").then(function(json){ 
   
	return json.post;
}).then(function(post){ 
   
//...
});

Promise.prototype.catch()

Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

getJSON("/posts.json").then(fuction(posts){ 
   
	//...
}).catch(function(error){ 
   
	// 处理getJSON 和 前一个回调函数运行时发生的错误
});

如果没有使用catch()方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应。

Promise.prototype.finally()

finally()方法用于指定不管Promise对象最后状态如何,都会执行的操作。该方法是ES2018引入标准的。
finally方法的回调函数不接受任何参数,这意味着它不知道前面的Promise实例的状态,这表明,finally方法里面的操作,应该是和状态无关的,不依赖于Promise的执行结果。
finally本质上是then方法的特例。

Promise.all()

Promise.all()方法用于将多个Promise实例,包装挣一个新的Promise实例。

const p = Promise.all([p1, p2, p3]);

参数Promise.all()方法接收一个数组作为参数,p1、p2、p3都是Promise实例,如果不是,则调用Promise.resolve方法,将参数转化为Promise实例。
Promise.all()方法的参数可以不是数组,但必须具有Iterator接口。
新实例p的状态由p1、p2、p3决定,有两种情况

  1. fulfilled只有p1、p2、p3的状态都变成fulfilledp的状态才会变成fulfilled,此时,它们的返回值组成一个数组,传递给p的回调函数
  2. rejected只要p1、p2、p3之中有一个被rejectedp的状态就变成了rejected,此时,第一个被reject的实例的返回值,会传递给p的回调函数

注意:如果作为参数的Promise实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()catch方法。

const p1 = new Promise((resolve, reject) => { 
   
  resolve('hello');
})
.then(result => result)
.catch(e => e);

const p2 = new Promise((resolve, reject) => { 
   
  throw new Error('报错了');
})
.then(result => result)
.catch(e => e);

Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 报错了]

原因p1resolvedp2rejected,但是p2有自己的catch方法,该方法返回的是一个新的Promise实例,p2实际上是这个实例。该实例执行完catch方法后,也会变成resolved,导致Promise.all()方法参数里面的两个实例都会resolved,因此会调用then方法指定的回调函数,而不会调用catch方法指定的回调函数。
如果p2没有自己的catch方法,就会调用Promise.all()catch方法。

Promise.race()

Promise.race()方法同样是将多个Promise实例,包装成一个新的Promise实例。

const p = Promise.race([p1, p2, p3]);

新实例只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。率先改变的Promise实例的返回值,就传递给p的回调函数
参数:和Promise.all()方法一样,如果不是Promise实例,就会调用Promise.resolve()方法转化。

const p = Promise.race([
  fetch('/resource-that-may-take-a-while'),
  new Promise(function (resolve, reject) { 
   
    setTimeout(() => reject(new Error('request timeout')), 5000)
  })
]);

p
.then(console.log)
.catch(console.error);

上面代码中,如果5秒内fetch方法无法返回结果,结果p的状态就会变为rejected,从而触发catch方法指定的回调函数。

Promise.allSettled()

Promise.allSettled()方法接收一组Promise实例作为参数,包装成一个新的Promise实例。只有等到所有这些参数实例都返回结果,无论是fulfilled还是rejected,包装实例才会结束。该方法由ES2020引入。
新实例:返回新的Promise实例,一旦结束,状态总是fulfilled,不会变成rejected。状态变成fulfilled后,Promise的监听函数接收到的参数是一个数组,每个成员对应传入一个Promise.allSettled()的Promise实例。

const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);

const allSettledPromise = Promise.allSettled([resolved, rejected]);

allSettledPromise.then(function (results) { 
   
  console.log(results);
});
// [
// { status: 'fulfilled', value: 42 },
// { status: 'rejected', reason: -1 }
// ]

使用Promise.allSettled()方法可以确定所有传入的Promise实例的异步操作是否结束了。

Promise.any()

Promise.any()方法接收一组Promise实例作为参数,包装成一个新的Promise实例。
新实例新实例的状态由所有参数实例决定,有两种情况

  1. fulfilled只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled
  2. rejected如果所有参数实例都变成rejected状态,包装状态就会变成rejected状态
var resolved = Promise.resolve(42);
var rejected = Promise.reject(-1);
var alsoRejected = Promise.reject(Infinity);

Promise.any([resolved, rejected, alsoRejected]).then(function (result) { 
   
  console.log(result); // 42
});

Promise.any([rejected, alsoRejected]).catch(function (results) { 
   
  console.log(results); // [-1, Infinity]
});

Promise.resolve()

Promise.resolve()方法可以把现有对象转为Promise对象。

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
  1. 参数是一个 Promise 实例
    不做任何修改。
  2. 参数是一个thenable对象
    thenable对象是指具有then方法的对象。如下:
let thenable = { 
   
  then: function(resolve, reject) { 
   
    resolve(42);
  }
};

转化为Promise对象,然后执行thenable对象的then方法。
3. 参数不是具有then方法的对象,或根本就不是对象

const p = Promise.resolve('Hello');

p.then(function (s){ 
   
  console.log(s)
});
// Hello

返回的Promise实例一生成就是resolved,所以回调函数会立即执行。
4. 不带有任何参数
Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的Promise对象(没有参数)。

const p = Promise.resolve();

p.then(function () { 
   
  // ...
});

Promise.reject()

Promise.reject(reason)方法也会返回一个新实例,该实例状态为rejected

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, function (s) { 
   
  console.log(s)
});
// 出错了

注意Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致。
如果传入一个thenable对象,那回调函数的参数就是这个对象。

应用

  1. 加载图片
    加载完,根据成功与否(Promise状态变化),去回调函数,进行后续操作。
const preloadImage = function (path) { 
   
  return new Promise(function (resolve, reject) { 
   
    const image = new Image();
    image.onload  = resolve;
    image.onerror = reject;
    image.src = path;
  });
};
  1. Generator函数与Promise的结合

Promise.try()

需求:不区分函数f是同步函数还是异步操作,但是想用Promise来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误。
一般写法

const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
// next
// now

一般写法缺点:如果f是同步函数,它会在本轮事件循环的末尾执行。
改进写法1

const f = () => console.log('now');
(async () => f())(); // 立即执行的匿名函数
console.log('next');
// now
// next

第二行的匿名函数立即执行里面的async函数,如果f同步就会得到同步结果,如果f异步,就可以用then指定下一步,catch捕获错误。如果不使用catch方法捕获错误,async() => f()会吃掉f()抛出的错误。
改进写法2

const f = () => console.log('now');
(
  () => new Promise(
    resolve => resolve(f())
  )
)();
console.log('next');
// now
// next

同样是立即执行的匿名函数,创建一个新的Promise实例,调用resolve方法,把f函数作为参数传入。这种情况下,同步函数也是同步执行的。
改进方法3

const f = () => console.log('now');
Promise.try(f);
console.log('next');
// now
// next

Promise.try()方法替代上面两种写法,为所有操作提供了统一的处理机制,还可以更好地管理异常。
管理异常
一个Promise对象抛出异步错误,可以用catch方法捕获,如下:

database.users.get({ 
   id: userId})
.then(...)
.catch(...)

但如果该对象抛出了同步错误,就需要使用try...catch去捕获。

try { 
   
  database.users.get({ 
   id: userId})
  .then(...)
  .catch(...)
} catch (e) { 
   
  // ...
}

使用Promise.try()方法生成的实例,可以统一调用promise.catch()捕获所有同步和异步的错误。

Promise.try(() => database.users.get({ 
   id: userId}))
  .then(...)
  .catch(...)

Promise.try模拟try代码块,promise.catch模拟catch代码块。

学习资料

ECMAScript 6 入门

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/139844.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 计算机中文字水印怎么设置(画图工具怎么擦除)

    win10系统使用久了,好多网友反馈说关于对win10系统使用画图功能去除图片水印设置的方法,在使用win10系统的过程中经常不知道如何去对win10系统使用画图功能去除图片水印进行设置,有什么好的办法去设置win10系统使用画图功能去除图片水印呢?在这里小编教你只需要1、选择一张你需要去除水印的图片;2、打开windows画图工具;就搞定了。下面小编就给小伙伴们分享一下对win10系统使用画图…

    2022年4月18日
    74
  • vue webpak版本 查看_vue版本以及webpack版本

    vue webpak版本 查看_vue版本以及webpack版本vue作为大前端的主流框架更新速度也是极快。那么vue的更新会有哪些问题呢?最近在搭建vue框架的时候发现由于vue版本的快速迭代已经与原本般配的webpack产生了隔阂。webpack作为大前端的主流打包工具如果与之不兼容,会有越来越多的麻烦事情。经过反复测试,得出结论一篇vue与webpack最佳拍档组合版本号公布。npminitnpminstallwebpack@3.10.0v…

    2022年6月1日
    428
  • C 调用 dll 文件

    C 调用 dll 文件一 先使用 C 来创建一个 dll 文件 1 创建新项目 CreateDll012 选择 动态链接库 dll 文件 3 在 CreateDll01 cpp 源文件中添加如下代码 CreateDll01 cpp 定义 DLL 应用程序的导出函数 include stdafx h extern C declspec dllex

    2025年6月17日
    2
  • 51单片机流水灯电路以及C代码「建议收藏」

    51单片机流水灯电路以及C代码「建议收藏」流水灯是51单片机的入门级实验,以下是其电路图以及C代码流水灯proteus电路图此图发光二极管采用共阳极式连接流水灯C代码#include<reg51.h>voiddelay1s(unsignedcharn);voidMovinglight(){ unsignedcharcodeMovinglightA

    2022年5月3日
    61
  • fork join原理_典型相关分析

    fork join原理_典型相关分析privateForkJoinPool(intparallelism,ForkJoinWorkerThreadFactoryfactory,UncaughtExceptionHandlerhandler,intmode,StringworkerNamePrefix)fo…

    2022年9月20日
    3
  • java面试题笔试常见选择题大全含答案

    java面试题笔试常见选择题大全含答案1、9道常见的java笔试选择题2、java多线程面试题选择题大全含答案3、java异常处理面试题常见选择题含答案4、java笔试常见的选择题(坑你没商量)5、java笔试题大全之IO流常见选择题6、java集合类笔试选择题整理含答案7、常见的javaweb笔试选择题含答案8、常见的SQL笔试选择题含答…

    2022年4月12日
    38

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号