Promise对象概述(什么是Promise)

Promise 是异步编程的一种解决方案,比传统的异步解决方案——回调函数和事件——更合理和更强大

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。

Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易

Promise 对象的特点

对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和Rejected(已失败)。

只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected

只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。

如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

Promise对象的缺点

无法取消Promise,一旦新建它就会立即执行,无法中途取消。

如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。

当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

如果某些事件不断地反复发生,一般来说,使用 Stream 模式是比部署Promise更好的选择。

Promise 对象的基本使用

ES6 规定,Promise对象是一个构造函数来生成Promise实例

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署

Promise 新建后就会立即执行里面的代码,不需要调用,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以Resolved最后输出

Promise的运行机制查看:js的运行机制这篇文章中的关于Promise运行机制

  1. let promise = new Promise(function (resolve,reject) {
  2. console.log("Promise");
  3. resolve(); //表示让Promise这个对象的状态从Pending变为Resolved
  4. });
  5.  
  6. promise.then(function () {
  7. console.log("执行完成")
  8. });
  9.  
  10. console.log("HI")

最后的执行结果是Promise—HI—执行完成

Promise的resolve函数和reject函数

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 Pending 变为 Resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去

  1. const imgs = [
  2. './1.jpg',
  3. './2.jpg',
  4. './3.jpg'
  5. ]
  6. let promise = new Promise(function (resolve, reject) {
  7. const img = new Image();
  8. img.src = '';
  9. img.onload = function () {
  10. resolve(this); //将异步操作的结果,作为参数传递出去
  11. };
  12. });

reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 Pending 变为 Rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去

  1. const imgs = [
  2. './1.jpg',
  3. './2.jpg',
  4. './3.jpg'
  5. ]
  6. let promise = new Promise(function (resolve, reject) {
  7. const img = new Image();
  8. img.src = '';
  9. img.onload = function () {
  10. resolve(this); //将异步操作的结果,作为参数传递出去
  11. };
  12. img.onerror = function () {
  13. reject(new Error('图片加载失败'));//将异步操作报出的错误,作为参数传递出去
  14. }
  15. });

Promise对象的两个原型方法then()和catch()

Promise.prototype.then(),Promise 实例具有then方法,就是说,then方法是定义在原型对象Promise.prototype上的它的作用是为 Promise 实例添加状态改变时的回调函数。

then方法的第一个参数是Resolved状态的回调函数,第二个参数(可选)是Rejected状态的回调函数(一般不用这个参数,使用catch()方法来获取错误的信息)。

  1. const imgs = [
  2. './1.jpg',
  3. './2.jpg',
  4. './3.jpg'
  5. ]
  6. let promise = new Promise(function (resolve, reject) {
  7. const img = new Image();
  8. img.src = imgs[0]; //将它改为'',也就是图片加载失败的时候,会触发then的第二个参数,获取错误信息
  9. img.onload = function () {
  10. resolve(this); //将异步操作的结果,作为参数传递出去
  11. };
  12. img.onerror = function () {
  13. reject(new Error('图片加载失败'));//将异步操作报出的错误,作为参数传递出去
  14. }
  15. });
  16.  
  17. promise.then(function (img) {
  18. console.log("图片加载完成");
  19. document.body.appendChild(img)
  20. },function (err) {
  21. console.log(err)
  22. })

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法(但是这个方法里面的代码不管异步执行成功还是失败,都会执行)

  1. const imgs = [
  2. './1.jpg',
  3. './2.jpg',
  4. './3.jpg'
  5. ]
  6. let promise = new Promise(function (resolve, reject) {
  7. const img = new Image();
  8. img.src = imgs[0]; //将它改为'',图片不存在的情况下,也就是图片加载失败的时候,会触发then的第二个参数,获取错误信息
  9. img.onload = function () {
  10. resolve(this); //将异步操作的结果,作为参数传递出去
  11. };
  12. img.onerror = function () {
  13. reject(new Error('图片加载失败'));//将异步操作报出的错误,作为参数传递出去
  14. }
  15. });
  16.  
  17. promise.then(function (img) {
  18. console.log("图片加载完成1");
  19. document.body.appendChild(img);
  20. },function (err) {
  21. console.log(err)
  22. }).then(function (img) {
  23. console.log("图片加载完成2");
  24. console.log(img)// undefined ,因为不是原来那个Promise实例了,所以获取不到这个img对象,不管是resolve还是reject这里都会执行
  25. })

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

  1. const imgs = [
  2. './1.jpg',
  3. './2.jpg',
  4. './3.jpg'
  5. ]
  6. let promise = new Promise(function (resolve, reject) {
  7. const img = new Image();
  8. img.src = ''; //将它改为'',也就是图片加载失败的时候,会触发then的第二个参数,获取错误信息
  9. img.onload = function () {
  10. resolve(this); //将异步操作的结果,作为参数传递出去
  11. };
  12. img.onerror = function () {
  13. reject(new Error('图片加载失败'));//将异步操作报出的错误,作为参数传递出去
  14. }
  15. });
  16.  
  17. promise.then(function (img) {
  18. console.log("图片加载完成1");
  19. document.body.appendChild(img);
  20. }).catch(function (err) {
  21. console.log(err);//Error: 图片加载失败
  22. });

promise过程分析

异步加载图片的例子

这个例子就是以上代码中的一个例子,这里是做一个封装,将这个例子封装成一个函数

  1. <!DOCTYPE html>
  2. <html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body></body></html>
  3. <script>
  4. const imgs = [
  5. './1.jpg',
  6. './2.jpg',
  7. './3.jpg'
  8. ]
  9.  
  10. function loadImageAsync(url) {
  11. var p = new Promise(function (resolve, reject) {
  12. const img = new Image();
  13. img.src = url;
  14. img.onload = function () {
  15. resolve(this);
  16. };
  17. img.onerror = function () {
  18. reject(new Error('图片加载失败')); //将异步操作的结果,作为参数传递出去
  19. };
  20. });
  21. return p
  22. }
  23.  
  24. console.log(123);
  25.  
  26. const allDone = Promise.all([loadImageAsync(imgs[0]),loadImageAsync(imgs[1]),loadImageAsync(imgs[2])]);
  27. allDone.then(function (datas) {
  28. console.log("图片加载完成")
  29. console.log(datas); //这个datas存放的是每个loadImageAsync方法传进来的参数的的一个集合
  30.  
  31. //遍历这个集合,并且放入页面的body标签内
  32. datas.forEach(function (item,i) {
  33. document.body.appendChild(item);
  34. })
  35. }).catch(function (err) {
  36. console.log(err)
  37. });
  38. console.log(456)
  39.  
  40. </script>

Promise对象的静态方法Promise.all()

Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例

Promise.all方法接受一个数组作为参数,并且数组里的元素都是 Promise 实例,如果不是,就会先调用Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。(Promise.all方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。)

  1. let p = Promise.all([p1,p2,p3])

p的状态由p1、p2、p3决定,分成两种情况。

只有p1、p2、p3的状态都变成Resolved,p的状态才会变成Resolved,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。(异步加载多张图片的示例)

  1. <!DOCTYPE html>
  2. <html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body></body></html>
  3. <script>
  4. const imgs = [
  5. './1.jpg',
  6. './2.jpg',
  7. './3.jpg'
  8. ]
  9.  
  10. function loadImageAsync(url) {
  11. var p = new Promise(function (resolve, reject) {
  12. const img = new Image();
  13. img.src = url;
  14. img.onload = function () {
  15. resolve(this);
  16. };
  17. img.onerror = function () {
  18. reject(new Error('图片加载失败')); //将异步操作的结果,作为参数传递出去
  19. };
  20. });
  21. return p
  22. }
  23.  
  24. console.log(123);
  25.  
  26. const allDone = Promise.all([loadImageAsync(imgs[0]),loadImageAsync(imgs[1]),loadImageAsync(imgs[2])]);
  27. allDone.then(function (datas) {
  28. console.log("图片加载完成")
  29. console.log(datas); //这个datas存放的是每个loadImageAsync方法传进来的参数的的一个集合
  30.  
  31. //遍历这个集合,并且放入页面的body标签内
  32. datas.forEach(function (item,i) {
  33. document.body.appendChild(item);
  34. })
  35. }).catch(function (err) {
  36. console.log(err)
  37. });
  38. console.log(456)
  39.  
  40. </script>

只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。(异步加载多张图片的示例,第二张图片加载失败)

  1. <!DOCTYPE html>
  2. <html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body></body></html>
  3. <script>
  4. const imgs = [
  5. './1.jpg',
  6. './2.jpg',
  7. './3.jpg'
  8. ]
  9.  
  10. function loadImageAsync(url) {
  11. var p = new Promise(function (resolve, reject) {
  12. const img = new Image();
  13. img.src = url;
  14. img.onload = function () {
  15. resolve(this);
  16. };
  17. img.onerror = function () {
  18. reject(new Error('图片加载失败')); //将异步操作的结果,作为参数传递出去
  19. };
  20. });
  21. return p
  22. }
  23.  
  24. console.log(123);
  25.  
  26. const allDone = Promise.all([loadImageAsync(imgs[0]),loadImageAsync(''),loadImageAsync(imgs[2])]);
  27. allDone.then(function (datas) {
  28. console.log("图片加载完成")
  29. console.log(datas); //这个datas存放的是每个loadImageAsync方法传进来的参数的的一个集合
  30.  
  31. //遍历这个集合,并且放入页面的body标签内
  32. datas.forEach(function (item,i) {
  33. document.body.appendChild(item);
  34. })
  35. }).catch(function (err) {
  36. console.log(err)
  37. });
  38. console.log(456)
  39.  
  40. </script>

Promise对象的静态方法Promise.resolve()

主要用来将现有对象转为Promise对象,参数是Promise对象的话,将不做任何修改,原封不动的返回这个对象

  1. <!DOCTYPE html>
  2. <html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body></body></html>
  3. <script>
  4. const imgs = [
  5. './1.jpg',
  6. './2.jpg',
  7. './3.jpg'
  8. ]
  9. function loadImageAsync(url) {
  10. var p = new Promise(function (resolve, reject) {
  11. const img = new Image();
  12. img.src = url;
  13. img.onload = function () {
  14. resolve(this);
  15. };
  16. img.onerror = function () {
  17. reject(new Error('图片加载失败')); //将异步操作的结果,作为参数传递出去
  18. };
  19. });
  20. return p
  21. }
  22. Promise.resolve(loadImageAsync(imgs[0])).then(function (img) {
  23. document.body.appendChild(img)
  24. })
  25. </script>

参数是一个thenable对象,thenable对象指的是具有then方法的对象,Promise.resolve方法会将这个对象转为Promise对象,然后就立即执行thenable对象的then方法

  1. <!DOCTYPE html>
  2. <html lang="en"><head><meta charset="UTF-8"><title>Title</title></head><body></body></html>
  3. <script>
  4. const imgs = [
  5. './1.jpg',
  6. './2.jpg',
  7. './3.jpg'
  8. ]
  9. Promise.resolve({
  10. then(resolve, reject){
  11. const img = new Image();
  12. img.src = imgs[0]; // 正常情况下,图片存在,不正常情况下,图片不存在
  13. img.onload = function () {
  14. resolve(this)
  15. };
  16. img.onerror = function () {
  17. reject(new Error('图片加载失败')); //将异步操作的结果,作为参数传递出去
  18. };
  19. }
  20. }).then(function (img) {
  21. document.body.appendChild(img);
  22. }).catch(function (err) {
  23. console.log(err)
  24. })
  25. </script>

如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的Promise对象,状态为Resolved

  1.   const p = Promise.resolve('Hello');
  2.  
  3. p.then(function (s){
  4. console.log(s) // Hello
  5. });

由于字符串Hello不属于异步操作(判断方法是字符串对象不具有then方法),返回Promise实例的状态从一生成就是Resolved,所以回调函数会立即执行。Promise.resolve方法的参数,会同时传给回调函数。

Promise.resolve方法允许调用时不带参数,直接返回一个Resolved状态的Promise对象。所以,如果希望得到一个Promise对象,比较方便的方法就是直接调用Promise.resolve方法

  1.   const p = Promise.resolve();
  2.  
  3. p.then(function () {
  4. console.log(p)
  5. });

实现一个完整的 Promise

下面我们来写一个简单的 promsie。Promise 的参数是函数 fn,把内部定义 resolve 方法作为参数传到 fn 中,调用 fn。当异步操作成功后会调用 resolve 方法,然后就会执行 then 中注册的回调函数。

ES5-ES6-ES7_Promise对象详解的更多相关文章

  1. es6 Reflect对象详解

    Reflect是ES6为操作对象而提供的新API,而这个API设计的目的只要有: 将Object对象的一些属于语言内部的方法放到Reflect对象上,从Reflect上能拿到语言内部的方法.如:Obj ...

  2. es6 Proxy对象详解

    Proxy用于修改某些操作的默认行为,也可以理解为在目标对象之前架设一层拦截,外部所有的访问都必须先通过这层拦截,因此提供了一种机制,可以对外部的访问进行过滤和修改.这个词的原理为代理,在这里可以表示 ...

  3. js对象详解(JavaScript对象深度剖析,深度理解js对象)

    js对象详解(JavaScript对象深度剖析,深度理解js对象) 这算是酝酿很久的一篇文章了. JavaScript作为一个基于对象(没有类的概念)的语言,从入门到精通到放弃一直会被对象这个问题围绕 ...

  4. JavaScript进阶知识点——函数和对象详解

    JavaScript进阶知识点--函数和对象详解 我们在上期内容中学习了JavaScript的基本知识点,今天让我们更加深入地了解JavaScript JavaScript函数 JavaScript函 ...

  5. jQuery的deferred对象详解

    jQuery的deferred对象详解请猛击下面的链接 http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_ ...

  6. Window 对象详解 转自 http://blog.csdn.net/jcx5083761/article/details/41243697

    详解HTML中的window对象和document对象 标签: HTMLwindowdocument 2014-11-18 11:03 5884人阅读 评论(0) 收藏 举报 分类: HTML& ...

  7. jQuery的deferred对象详解(转载)

    本文转载自: jQuery的deferred对象详解(转载)

  8. mvc-servlet---ServletConfig与ServletContext对象详解(转载)

    ServletConfig与ServletContext对象详解 一.ServletConfig对象    在Servlet的配置文件中,可以使用一个或多个<init-param>标签为s ...

  9. JS中的event 对象详解

    JS中的event 对象详解   JS的event对象 Event属性和方法:1. type:事件的类型,如onlick中的click:2. srcElement/target:事件源,就是发生事件的 ...

  10. JavaWeb学习----JSP内置对象详解

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/4 ...

随机推荐

  1. 在.net中怎么解析json串 [Error reading JObject from JsonReader. Current JsonReader item is not an obj]

    编辑时间:2017-05-10,增加一种转化list的方法 一.以前知道一种解析json串的方法,觉得有点麻烦.就从别的地方搜到了另一种 string json = vlt.getlist(); JO ...

  2. 从零开始学安全(十三)●SQL server 2008 R2 安装

    安装过程1.下载并解压 sql_server_2008_r2_enterprise 点击 setup . 2.打开后如图,点击左侧的 安装 ,再点击右边的 全新安装或向现有安装添加功能. 3.安装支持 ...

  3. Android Studio 基础控件使用

    TextView android:gravity="center" //文字对其方式 top bottom left right center android:textColor= ...

  4. C# 使用 PerformanceCounter 获取 CPU 和 硬盘的使用率

    C# 使用 PerformanceCounter 获取 CPU 和 硬盘的使用率: 先看界面: 建一个 Windows Form  桌面程序,代码如下: using System; using Sys ...

  5. [android] android消息机制入门

    上一节,先把访问网络的部分放到一个子线程里面去执行,new Thread(){}.start(),new Thread直接使用匿名内部类来实现,重写run()方法,内部类访问外部的变量,这个变量应该定 ...

  6. [nodejs] nodejs开发个人博客(三)载入页面

    模板引擎 使用ejs作为我们博客的前端模板引擎,用来从json数据生成html字符串 安装:npm install ejs -save 使用:入口文件中写入下面代码,定义/view/目录为视图目录 / ...

  7. Linux-read 命令(20)

    Linux read 命令 参数说明: -a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符. -d 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志. -p ...

  8. hihoCoder编程练习赛52

    题目1 : 字符串排序 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 一般我们在对字符串排序时,都会按照字典序排序.当字符串只包含小写字母时,相当于按字母表" ...

  9. #WEB安全基础 : HTML/CSS | 0x4HTML模块化

    想让你的网页变得整洁吗?找我就对了,当然你会认识几个新元素,和它们交朋友吧! 我帮你联系一下这几个新元素,这样交朋友就变得简单了 images里放着图片   以下是index.html的代码 < ...

  10. canvas-8searchLight4.html

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...