1. Promise基础

什么是回调地狱?

当使用回调函数来进行事件处理的时候,如果嵌套多层回调函数的时候,就会出现回调地狱,例如:

method1(function(err, result) {
if (err) {
throw err;
}
method2(function(err, result) {
if (err) {
throw err;
}
method3(function(err, result) {
if (err) {
throw err;
}
method4(function(err, result) {
if (err) {
throw err;
}
method5(result);
});
});
});
});

像本例一样嵌套多个方法调用会创建错综复杂的代码,会难以理解与调试。当想要实现更复

杂的功能时,回调函数也会存在问题。要是你想让两个异步操作并行运行,并且在它们都结

束后提醒你,那该怎么做?要是你想同时启动两个异步操作,但只采用首个结束的结果,那

又该怎么做?而使用Promise就能避免回调地狱的情况。

Promise可以当做是一个占位符,表示异步操作的执行结果。函数可以返回一个Promise,而不必订阅一个事件或者向函数传递一个回调函数。

Promise的生命周期

每个 Promise 都会经历一个短暂的生命周期,初始为挂起状态(pending state) ,这表示异步操作尚未结束。一个挂起的 Promise 也被认为是未决的(unsettled )。一旦异步操作结束, Promise就会被认为是已决的(settled ) ,并进入两种可能状态之一:

  1. 已完成(fulfilled ) : Promise 的异步操作已成功结束;
  2. 已拒绝(rejected ) : Promise 的异步操作未成功结束,可能是一个错误,或由其他原因导致。

内部的[[PromiseState]] 属性会被设置为"pending""fulfilled" 或 "rejected",以反映Promise的状态。该属性并未在 Promise 对象上被暴露出来,因此你无法以编程方式判断 Promise 到底处于哪种状态。不过你可以使用then()方法在 Promise 的状态改变时执行一些特定操作。

  1. then()方法

    then()方法在所有的 Promise 上都存在,并且接受两个参数。第一个参数是 Promise 被完成时要调用的函数,异步操作的结果数据都会被传入这个完成函数。第二个参数则是 Promise 被拒绝时要调用的函数,与完成函数相似,拒绝函数会被传入与拒绝相关联的任何附加数据。then()方法的两个参数是可选的,因此可以自由组合监听完成和失败的处理函数;

  2. catch()方法

    Promise有catch()方法,等同于只传递拒绝处理函数给then()方法:

     promise.catch(function(err) {
    // 拒绝
    console.error(err.message);
    });
    // 等同于:
    promise.then(null, function(err) {
    // 拒绝
    console.error(err.message);
    });

创建未决的Promise

使用Promise构造器可以创建一个Promise实例,此构造器接收一个参数:一个被称之为执行器(excutor)的函数,该函数包含了resolve()函数和reject()函数这两个参数。resolve()函数在异步任务执行成功时调用,而reject()函数在异步任务执行失败时调用。例如:

let promise = new Promise(function(resolve,reject){
console.log('hi, promise');
resolve(); }); promise.then(()=>{
console.log('hi, then'); }); console.log('hi'); 输出:
hi, promise
hi
hi then

从输出结果可以看出,Promise构造器中的代码是最先执行的,而then()代码是最后执行的,这是因为只有在Promise中的处理器函数执行结束之后,then()方法中的完成处理函数或者拒绝处理函数才会添加到作业队列的尾部。

创建已决的Promise

  1. 使用Promise.resolve()

Promise.resolve()方法接收一个参数,并会返回一个处于已完成状态的 Promise ,在then()方法中使用完成处理函数才能提取该完成态的Promise传递的值,例如:

let promise = Promise.resolve('hi');
promise.then((value)=>{
console.log(value); //hi
});
  1. 使用Promise.reject()

可以使用Promise.reject()方法来创建一个已拒绝状态的Promise,同样只有在拒绝处理函数中或者catch()方法中才能接受reject()方法传递的值:

let reject = Promise.reject('reject');

reject.catch((value)=>{
console.log(value); //reject
})

非Promise的thenable

当一个对象拥有一个能接受resolvereject参数的then()方法时,该对象就会被认为是一个非Promisethenable,例如:

let thenable = {

    then:function(resolve,reject){
resolve('hi');
}
}

Promise.resolve()Promise.reject()方法都能够接受非Promise的thenable作为参数,当传入了非Promise的thenable时,这些方法会创建一个新的Promise,并且可以使用then()方法对不同状态进行操作:

创建一个已完成的Promise

let thenable = {

    then:function(resolve,reject){
resolve('hi');
}
} let promise = Promise.resolve(thenable);
promise.then((value)=>{
console.log(value); //hi
});

同样利用thenable可以创建一个已拒绝的Promise:

let thenable = {

    then:function(resolve,reject){
reject('hi');
}
} let promise = Promise.resolve(thenable);
promise.then(null,(value)=>{
console.log(value);
});

执行器错误

当执行器内部抛出错误,那么Promise的拒绝处理函数就会被调用,例如:

let promise = new Promise(function(resolve,reject){
throw new Error('Error!'); }) promise.catch(function(msg){
console.log(msg); //error
})

2. Promise链

除了使用单个Promise外,多个Promise可以进行级联使用,实际上then()方法或者catch()方法会返回一个新的Promise,仅当前一个Promise被决议之后,后一个Promise才会进行处理。

串联Promise

let p1 = new Promise(function(resolve,reject){
resolve('hi');
}); p1.then((value)=>{
console.log(value);
throw new Error('Error!');
}).catch(function(error){
console.log(error);
})

可以看出当p1的then()方法执行结束后会返回一个Promise,因此,在此基础上可以继续执行catch()方法。同时,Promise链允许捕获前一个Promise的错误

Promise链中传递值

Promise链的另一个重要方面是能从一个Promise传递数据给另一个Promise的能力。前一个Promise的完成处理函数的返回值,传递到下一个Promise中。

//Promise链传递值

let p1 = new Promise(function(resolve,reject){
resolve(1);
}) p1.then(value=>value+1)
.then(value=>{
console.log(value);
})

p1的完成处理函数返回了value+1,也就是2,会传入到下一个Promise的完成处理函数,因此,第二个then()方法中的完成处理函数就会输出2。拒绝处理函数同样可以被用于在Promise链中传递数据。

Promise链中传递Promise

在完成或者拒绝处理函数中可以返回基本类型值,从而可以在Promise链中传递。另外,在Promise链中也可以传递对象,如果传递的是Promise对象,就需要额外的处理:

传递已完成状态的Promise

let p1 = new Promise(function(resolve,reject){
resolve(1);
}); let p2 = new Promise(function(resolve,reject){
resolve(2);
}) p1.then(value=>{
console.log(value);
return p2;
}).then(value=>{
console.log(value);
});
输出:1 2

p1中返回了Promise对象p2,当p2完成时,才会调用第二个then()方法,将值value传到完成处理函数中。若Promise对象p2被拒绝后,第二个then()方法中的完成处理函数就不会执行,只能通过拒绝处理函数才能接收到p2传递的值:

let p1 = new Promise(function(resolve,reject){
resolve(1);
}); let p2 = new Promise(function(resolve,reject){
reject(2);
}) p1.then(value=>{
console.log(value);
return p2;
}).catch(value=>{
console.log(value);
});

3. 响应多个Promise

如果想监视多个Promise的状态,从而决定下一步动作,可以使用ES6提供的两个方法:Promise.all()Promise.race()

Promise.all()

Promise.all()方法能接受单个可迭代对象(如数组)作为参数,可迭代对象的元素都是Promise。该方法会返回一个Promise,只有传入所有的Promise都已完成,所返回的Promise才会完成,例如:

//Promise.all()
let p1 = new Promise(function(resolve,reject){
resolve(1);
}) let p2 = new Promise(function(resolve,reject){
resolve(2);
}) let p3 = new Promise(function(resolve,reject){
resolve(3);
}) let p4 = Promise.all([p1,p2,p3]);
p4.then(value=>{
console.log(Array.isArray(value)); //true
console.log(value); //[1,2,3]
})

Promise.all() 的调用创建了新的Promise p4,在 p1p2p3 都被完成后, p4 最终会也被完成。传递给 p4 的完成处理函数的结果是一个包含每个决议值(1 、 2 与 3 ) 的数组,这些值的存储顺序保持了待决议的 Promise 的顺序(与完成的先后顺序无关) ,因此你可以将结果匹配到每个Promise

若传递给Promise.all() 的某个 Promise 被拒绝了,那么方法所返回的 Promise 就会立刻被拒绝,而不必等待其他的 Promise 结束

//Promise.all()
let p1 = new Promise(function(resolve,reject){
resolve(1);
}) let p2 = new Promise(function(resolve,reject){
reject(2);
}) let p3 = new Promise(function(resolve,reject){
resolve(3);
}) let p4 = Promise.all([p1,p2,p3]);
p4.catch(value=>{
console.log(Array.isArray(value)); //true
console.log(value); //2
})

在此例中, p2 被使用数值 2 进行了拒绝,则 p4 的拒绝处理函数就立刻被调用,而不会

等待 p1 或 p3 结束执行(它们仍然会各自结束执行,只是 p4 不等它们) 。

拒绝处理函数总会接受到单个值,而不是一个数组。该值是被拒绝的Promise所返回的拒绝值。

Promise.race()

Promise.race()方法接收一个元素是Promise的可迭代对象,并返回一个新的Promise。一旦传入Promise.race()的可迭代对象中有一个Promise是已决状态,那么返回的Promise对象就会立刻成为已决状态。

Promise.all()方法得必须等到所有传入的Promise全部变为已决状态,所返回的Promise才会已决。

let p1 = new Promise(function(resolve,reject){
resolve(1);
}) let p2 = new Promise(function(resolve,reject){
resolve(2);
}) let p3 = new Promise(function(resolve,reject){
resolve(3);
}) let p4 = Promise.race([p1,p2,p3]);
p4.then(value=>{
console.log(Array.isArray(value)); //false
console.log(value); //1
})

Promise.race() 方法传入的Promise中哪一个Promise先变成已完成状态,就会将值传递给所返回的Promise对象的完成处理函数中。若哪一个Promise最先变成已拒绝状态,同样的,会将值传递给p4的拒绝处理函数中。

4. 继承Promise

可以继承Promise实现自定义的Promise,例如:

class MyPromise extends Promise {
// 使用默认构造器
success(resolve, reject) {
return this.then(resolve, reject);
}
failure(reject) {
return this.catch(reject);
}
}
let promise = new MyPromise(function(resolve, reject) {
resolve(42);
});
promise.success(function(value) {
console.log(value); // 42
}).failure(function(value) {
console.log(value);
});

在此例中, MyPromise 从 Promise 上派生出来,并拥有两个附加方法。 success() 方法模拟了 resolve()failure() 方法则模拟了 reject()

5. 总结

  1. Promise 具有三种状态:挂起、已完成、已拒绝。一个 Promise 起始于挂起态,并在成功时转为完成态,或在失败时转为拒绝态。 then() 方法允许你绑定完成处理函数与拒绝处理函数,而 catch() 方法则只允许你绑定拒绝处理函数;

  2. 能够将多个Promise串联起来组成Promise链,并且能够在中间传递值,甚至是传递Promise对象。 then() 的调用都创建并返回了一个新的 Promise ,只有在前一个 Promise 被决议过,新 Promise 也会被决议。 同时也可以使用Promise.all()和Promise.race()方法来管理多个Promise。

                                                                                  

                                                                                              飞机票:https://www.jianshu.com/p/df667b84b121

深入了解promise的更多相关文章

  1. Javascript - Promise学习笔记

    最近工作轻松了点,想起了以前总是看到的一个单词promise,于是耐心下来学习了一下.   一:Promise是什么?为什么会有这个东西? 首先说明,Promise是为了解决javascript异步编 ...

  2. 路由的Resolve机制(需要了解promise)

    angular的resovle机制,实际上是应用了promise,在进入特定的路由之前给我们一个做预处理的机会 1.在进入这个路由之前先懒加载对应的 .js $stateProvider .state ...

  3. angular2系列教程(七)Injectable、Promise、Interface、使用服务

    今天我们要讲的ng2的service这个概念,和ng1一样,service通常用于发送http请求,但其实你可以在里面封装任何你想封装的方法,有时候控制器之间的通讯也是依靠service来完成的,让我 ...

  4. 闲话Promise机制

    Promise的诞生与Javascript中异步编程息息相关,js中异步编程主要指的是setTimout/setInterval.DOM事件机制.ajax,通过传入回调函数实现控制反转.异步编程为js ...

  5. 深入理解jQuery、Angular、node中的Promise

    最初遇到Promise是在jQuery中,在jQuery1.5版本中引入了Deferred Object,这个异步队列模块用于实现异步任务和回调函数的解耦.为ajax模块.队列模块.ready事件提供 ...

  6. Promise的前世今生和妙用技巧

    浏览器事件模型和回调机制 JavaScript作为单线程运行于浏览器之中,这是每本JavaScript教科书中都会被提到的.同时出于对UI线程操作的安全性考虑,JavaScript和UI线程也处于同一 ...

  7. JavaScript进阶之路——认识和使用Promise,重构你的Js代码

    一转眼,这2015年上半年就过去了,差不多一个月没有写博客了,"罪过罪过"啊~~.进入了七月份,也就意味着我们上半年苦逼的单身生活结束了,从此刻起,我们要打起十二分的精神,开始下半 ...

  8. 细说Promise

    一.前言 JavaScript是单线程的,固,一次只能执行一个任务,当有一个任务耗时很长时,后面的任务就必须等待.那么,有什么办法,可以解决这类问题呢?(抛开WebWorker不谈),那就是让代码异步 ...

  9. 浅谈Angular的 $q, defer, promise

    浅谈Angular的 $q, defer, promise 时间 2016-01-13 00:28:00  博客园-原创精华区 原文  http://www.cnblogs.com/big-snow/ ...

  10. angular学习笔记(二十八-附2)-$http,$resource中的promise对象

    下面这种promise的用法,我从第一篇$http笔记到$resource笔记中,一直都有用到: HttpREST.factory('cardResource',function($resource) ...

随机推荐

  1. DEV GridControl小结。。

    [转]DEV GridControl小结.. 来自:http://www.cnblogs.com/yuerdongni/archive/2012/09/08/2676753.html   1. 如何解 ...

  2. Codeforces 567D:One-Dimensional Battle Ships(二分)

    time limit per test : 1 second memory limit per test : 256 megabytes input : standard input output : ...

  3. Linux压缩和解压类

    gzip/gunzip gzip 文件:压缩文件为.gz gunzip 文件:解压文件 注: 1. 只能压缩和解压文件 2. 只是将hello.txt压缩成hello.txt.gz,并没有改变存储位置 ...

  4. 基于Spring MVC + Spring + MyBatis的【外包人力资源管理系统】

    资源下载:https://download.csdn.net/download/weixin_44893902/45600390 练习点设计:模糊查询.删除.新增 一.语言和环境 实现语言:JAVA语 ...

  5. SpringMVC+Spring+Mybatis实现登录注册Demo

    使用环境:MyEclipse/Eclipse + Tomcat + MySql. 使用技术:SpringMVC + Spring + Mybatis. 实现效果 登录页面: 密码错误提示 登录成功后 ...

  6. Vue-cli3.0配置全局less

    第一种配置方式(推荐) npm install style-resources-loader vue-cli-plugin-style-resources-loader less-loader les ...

  7. Java高级程序设计笔记 • 【第3章 多线程(二)】

    全部章节   >>>> 本章目录 3.1 同步代码块 3.1 线程安全 3.1.1 模拟银行取款 3.1.2 同步代码块的使用 3.1.3 实践练习 3.2 同步方法 3.2. ...

  8. 加深理解Java异常概念并熟记5个最常见的运行时异常

    加深理解Java异常概念并熟记5个最常见的运行时异常 说明Error与Exception的联系和区别有哪些? 列举最常见的5个运用时异常. 1.Error和Exception的联系和区别: Error ...

  9. Hbase单点安装Version1.1.5

    Hbase单点安装,基于版本1.1.5, 使用hbase-1.1.5.tar.gz安装包. 1.安装说明 使用Hbase自带zookeeper和本地文件目录存储数据 2.安装规划 角色规划 IP/机器 ...

  10. [学习笔记] Oracle基础增删改查用法

    查询 select *|列名|表达式 from 表名 where 条件 order by 列名 select t.* from STUDENT.STUINFO t where t.stuname = ...