什么是promise
promise 翻译是 承诺,许诺 的意思,promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了promise对象。
所谓promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,promise 是一个对象,从它可以获取异步操作的消息。promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理
- 没有异步就不需要
promise。 promise本身不是异步,只是我们去编写异步代码的一种方式
我们知道JavaScript中所有代码都是单线程执行的,所以导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。
异步执行可以用回调函数实现,功能没有任何问题,但是这个写法比较讨厌--- 这就是臭名昭著的回调地狱(callback hell)。 针对这个写法,非常的糟糕,需要有一种机制,让这个写法变得更加优雅一些。
所以,在前端社区中,就有一些牛人,纷纷提供了一些新的写法,以避免回调地狱。
后来,es6就在这些基础之上,将promise直接纳入的规范。从而成为es6中的标准用法
promise A+规范
由于promise本身出自于民间,就有很多不同的实现版本。
Es6将promise纳入自己规范的时候,也遵循了一个相应的标准 -- promise A+规范。
将其归纳为4321规范。
- 4:4大术语
- 3:3种状态
- 2:2种事件
- 1:1个对象
4大术语
一定要结合异步操作来理解。 既然是异步,这个操作需要有个等待的过程,从操作开始,到获取结果,有一个过程的。
解决(fulfill):指一个 promise 成功时进行的一系列操作,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但在后世的 promise 实现多以 resolve 来指代之。
拒绝(reject):指一个 promise 失败时进行的一系列操作。
终值(eventual value):所谓终值,指的是 promise 被解决时传递给解决回调的值,由于 promise 有一次性的特征,因此当这个值被传递时,标志着 promise 等待态的结束,故称之终值,有时也直接简称为值(value)。
据因(reason):也就是拒绝原因,指在 promise 被拒绝时传递给拒绝回调的值。
3种状态
当操作完成时,就有相应的结果,结果有两种:
- 成功了
- 失败了
一共是3种状态,如下:
- 等待态(Pending)
- 执行态(Fulfilled)
- 拒绝态(Rejected)

针对每一种状态,有一些规范:
等待态(
Pending) 处于等待态时,promise需满足以下条件:
- 可以迁移至执行态或拒绝态
执行态(
Fulfilled) 处于执行态时,promise需满足以下条件:
- 不能迁移至其他任何状态
- 必须拥有一个不可变的终值
拒绝态(
Rejected) 处于拒绝态时,promise需满足以下条件:
- 不能迁移至其他任何状态
- 必须拥有一个不可变的据因
2种事件
针对3种状态,只有如下两种转换方向:
- pending --> fulfilled
- pendeing --> rejected
在状态转换的时候,就会触发事件。
- 如果是pending --> fulfiied,就会触发onFulFilled事件
- 如果是pendeing --> rejected,就会触发onRejected事件
1个对象
就是指promise对象
promise使用
在es6中,提供了promise构造器,这个promise构造器,必须要传递一个参数。
构造器的参数,是一个回调函数,包含两个参数: resolve , reject
promise实例化
实例化
let a = new promise((resolve, reject) => {
// resolve("ok")
reject("NOK")
})
当前这个对象处于 pending 状态。
回调函数中的两个参数,其作用就是用于转换状态:
resolve ,将状态从pending --> fullFilled
reject ,将状态从pending --> rejected
直接使用函数调用的方式来进行转换,在转换的时候必须要传递相应参数
- Resolve函数的参数,就是指 终值(value)
- Reject函数的参数,就是指 据因(reason)
then方法
let a = new Promise((resolve, reject) => {
resolve("ok") // 终值
reject("NOK") //据因
})
怎么获取这里的终值和据因呢?
引入事件机制,三种状态,只有两种转换方向
在状态转换的时候,就会触发事件。
如果是pending --> fulfiied,就会触发onFulFilled事件 如果是pendeing --> rejected,就会触发onRejected事件
在调用resolve方法或者reject方法的时候,就一定会触发事件。 需要注册onFulFilled事件 和 onRejected事件
针对事件的注册,Promise对象提供了then方法,如下:
promise.then(onFulFilled,onRejected)
a.then(function (res) {
console.log("ok")
}, function (err) {
console.log("NOK")
})
catch方法
promise then方法也有语法糖 --->> catch
如上,then方法需要写两个回调方法,promise.then(onFulFilled,onRejected),辨识度不高,所以promise提供了一个catch方法,用于注册 onRejected回调。
let a = new Promise((resolve, reject) => {
if (1) {
resolve("开始")
} else {
reject("结束")
}
})
a.then(data => console.log(data + 'start'));
a.catch(err => console.log(err + 'end'));
catch其实是then的简写,then(null,callback)
a.then(data => console.log(data + 'start'));
a.then(null, err => console.log(err + 'start'));
//a.catch(err => console.log(err + 'end'));
then方法调用之后,仍然返回的是promise对象,所以可以链式调用
a.then(data => console.log(data)).catch(err => console.log(err))
所以,我们在使用promise对象时,一般这么描述,异步操作成功的时候,走then,失败的时候就走catch。
then方法链式调用
then方法的链式调用非常有趣,简单的说,在then方法后面继续的调用then方法,这就是then的链式调用
let fs = require("fs")
function readFile(...args){
return new Promise((resolve,reject)=>{
fs.readFile(...args,function(err,data){
if(err) reject(err)
resolve(data)
})
});
}
// 如果返回的是一个promise,那么这个promise会执行,并且会采用它的状态
readFile("./name.txt","utf8").then(data=>{
// return data; return readFile(data,"utf8")
return readFile(data,"utf8")
},err=>{
return err;
}).then(data=>{
console.log(data) // 666
},err=>{
console.log(err)
})
// 链式调用 如果在上一个then的第一个函数中,返回一个普通值,会走到下一个then的第1个函数,return的值作为这个then的data
// 如果返回的是一个promise,会作为下一个then的promise对象,data err去promise对象中取
then方法返回的是一个promise对象,但它是一个新的promise对象,不再是原来的promise对象
前一个then方法的返回值,或作为下一个then方法的参数(普通参数,promise对象例外)
如果返回的是promise对象,就作为下一次调用then方法的promise对象
then方法中回调函数的return 返回值,和then方法本身的返回值是两码事
可以在后面添加一个catch方法,链式调用then方法时,有错直接不再继续并执行错误处理函数。
promise定义典例
一点简单栗子直观感受一下
读取文件
//const fs = require('fs')
//var rs = new Promise((resolve, reject) => {
// fs.readFile('name.txt', 'utf8', (err, data) => {
// if (err) {
// reject(err)
// } else {
// resolve(data)
// }
// })
//})
//rs.then(data => console.log(data))
// .catch(err => console.log(err))
//在开发的时候,我们定义promise对象,必须是要作为函数的返回值。
const fs = require('fs')
function readFile(file) {
return new Promise((resolve, reject) => {
fs.readFile(file, 'utf8', (err, data) => {
if (err) {
reject(err)
} else {
resolve(data)
}
})
})
}
readFile('name.txt').then(data => console.log(data))
.catch(err => console.log(err))
根据随机数返回结果
var rs = new Promise((resolve, reject) => {
setTimeout(() => {
var n = Math.random()
console.log(n)
if (n > 0.5) {
resolve("n比较大")
} else {
reject("n比较小")
}
}, 1000)
})
rs.then(res => console.log(res)).catch(err => console.log(err))
清晰明了,promise在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了

all 和 race 方法
除了串行执行若干异步任务外,Promise还可以并行执行异步任务,可以使用Promise.all()方法和Promise.race() 方法。
并行执行两个任务,如果有任何一个失败,就不进行,用Promise.all()
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 400, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后才执行then:
Promise.all([p1, p2]).then(function (results) {
console.log(results); // 获得一个Array: ['P1', 'P2']
});
有些时候,多个异步任务是为了容错,即开多个任务可以得到相同的结果,只需要获得先返回的结果即可,用Promise.race()
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 400, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
console.log(result); // 'P1'
});
// 由于p1执行较快,Promise的then()将获得结果'P1'。p2仍在继续执行,但执行结果将被丢弃。
一点小cones
思考:catch方法放在then方法前会发生什么? 还是用上面的栗子
var rs = new Promise((resolve, reject) => {
setTimeout(() => {
var n = Math.random()
console.log(n)
if (n > 0.5) {
resolve("n比较大")
} else {
reject("n比较小")
}
}, 1000)
})
rs.catch(err => console.log(err)).then(res => console.log(res))
// rs.then(res => console.log(res)).catch(err => console.log(err))
好,第一次Math.random()返回了一个n>0.5 的数,执行then()
结果:n比较大
再来,终于Math.random()返回一个n<0.5 的数
结果:n比较小,undefined
Promise 不论成功或失败都会调用 then 然而catch() 只有当 promise 失败时才会调用
因为 catch 的回调函数没有返回值,所以 catch 方法返回的 promise 对象的状态变为了 'resolved',value 值变为了 undefined
Promise().then().then....catch() 多任务串行执行.
情景化记忆:在一个任务链中,比如我要向上级部门一层层的往上提交申请,if(某种条件)承诺帮你resolve解决问题,else承诺reject你的请求. 他给出的resolve问题的办法只是个空头Promise,then到总经理那实现具体承诺,如果总经理还是给一个空头承诺(返回Promise实例),还得then到董事长那里.... 任一一步做出的是reject的承诺,还有什么好说的,被拒绝了,后面的就不会再往上走了呀. 准备catch 拒绝通知吧blablabla
Promise.all([p1,p2,...]) 多任务并行执行都要成功才进入then,返回结果数组.
Promise.race([p1,p2,...])多任务赛跑then()和catch(),谁先调用算谁的,其它任务中断.