一、promise的起源
大家都知道nodejs很快,原因就是node采用异步回调的方式来处理需要等待的事件,使得代码会继续往下执行不用在某个地方等待着。但是也有一个不好的地方,当我们有很多回调的时候,比如这个回调执行完需要去执行下个回调,然后接着再执行下个回调,这样就会造成层层嵌套,代码不清晰,很容易进入“回调监狱”,就容易造成下边的例子:
请求1(function(请求结果1){
请求2(function(请求结果2){
请求3(function(请求结果3){
请求4(function(请求结果4){
请求5(function(请求结果5){
请求6(function(请求结果3){
...
})
})
})
})
})
})
这样的写法代码缺点很多。es6新出的promise对象已经es7的async await都可以解决这个问题,当然这里先介绍promise对象,es7的async await将在后边的文章中分享。下边将来介绍Promise对象。
二、什么是promise(概念了解)
Promise 是异步编程的一种解决方案,比传统的异步解决方案【回调函数】和【事件】更合理、更强大。现已被 ES6 纳入进规范中。
Promise,他是一个对象,是用来处理异步操作的,可以让我们写异步调用的时候写起来更加优雅,更加美观便于阅读。顾名思义为承诺、许诺的意思,意思是使用了Promise之后他肯定会给我们答复,无论成功或者失败都会给我们一个答复,所以我们就不用担心他跑了哈哈。
所以,Promise有三种状态:pending(进行中),resolved(完成),rejected(失败)。只有异步返回的结构可以改变其状态。
所以,promise的过程一般只有两种:pending->resolved或者pending->rejected。
此时,promise用来解决的问题:
- 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
- promise可以支持多个并发的请求,获取并发请求中的数据
注意:这个promise可以解决异步的问题,本身不能说promise是异步的
三、promise的API(静态方法)
3.1、创建一个promise
// promise本身是同步的
console.log("start")
let p = new Promise(()=>{
console.log("哈哈") // 哈哈
})
console.log("end") // start 哈哈 end
// 在执行器内部也是可以写异步代码
// then中的方法什么时候调用,只有当调用resolve,reject才会去执行then中的方法
let p = new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("setTimeout")
// resolve("包")
reject("没钱了...")
},1000)
})
p.then((data)=>{
console.log(data) // 包
},(err)=>{
console.log(err) // 没钱了...
})
只有当resolve,reject才会执行then中的方法,执行resolve成功时,会调用.then方法,reject会执行err.
Promise的构造函数接收一个参数:函数,并且这个函数需要传入两个参数:
- resolve :异步操作执行成功后的回调函数
- reject:异步操作执行失败后的回调函数
3.2、resolve方法和reject方法
它们的作用其实就是返回一个Promise对象,我们来实现一下。
- resolve
//resolve方法
Promise.resolve = function(val){
return new Promise((resolve,reject)=>{
resolve(val)
})
}
- reject
//reject方法
Promise.reject = function(val){
return new Promise((resolve,reject)=>{
reject(val)
})
}
3.3、then方法的实现链式调用
-
Promise.prototype.then
该实例方法挂到原型上的,为 Promise 注册回调函数,函数形式:fn(vlaue){},value 是上一个任务的返回结果,then 中的函数一定要 return 一个结果或者一个新的 Promise 对象,才可以让之后的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)
})
});
}
// 这样写不优雅, 又回到了回调地域 XXXXXXXXXXXX
readFile("./name.txt","utf8").then(data=>{
// console.log(data) // age.txt
readFile(data,"utf8").then(data=>{
console.log(data) // 666
},err=>{
console.log(err)
})
},err=>{
console.log(err)
})
所以 .then 后面还可以 .then ,其实.then之后又返回了一个新的promise,而新的promise又可以.then了。 并且下一个.then中的data时上一个.then中的return data.err也是一样的。
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对象中取
3.4 Promise.prototype.catch
该方法是挂在Promise原型上的方法。当我们调用catch传callback的时候,就相当于是调用了then方法。
let p = new Promise((resolve,reject)=>{
resolve("OK")
})
// catch就.then的语法糖
// 最终,一个promise中,一般在then中只有一个函数,在then后面有一个catch,一般使用then来获取data,在catch中获取err
p.then(data=>{
console.log(data)
}).catch(err=>{
console.log(err)
})
3.5、Promise.all
all方法可以说是Promise中很常用的方法了,它的作用就是将一个数组的Promise对象放在其中,当全部resolve的时候就会执行then方法,当有一个reject的时候就会执行catch,并且他们的结果也是按着数组中的顺序来排放的,那么我们来实现一下。
let Promise1 = new Promise(function(resolve, reject){})
let Promise2 = new Promise(function(resolve, reject){})
let Promise3 = new Promise(function(resolve, reject){})
let p = Promise.all([Promise1, Promise2, Promise3])
p.then(funciton(){
// 三个都成功则成功
}, function(){
// 只要有失败,则失败
})
3.6、Promise.race
顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
- 原理:
//race方法
Promise.race = function(promises){
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
promises[i].then(resolve,reject)
}
})
}
- 用法:
let fs = require("fs").promises;
Promise.race([fs.readFile("./name.txt","utf-8"),fs.readFile("./age.txt","utf-8")]).then(data=>{
console.log(data) // age.txt
})
3.7 finally方法:
//finally 最终 不管转成成功态还是失败态,都会调用finally这个方法
Promise.resolve("包包").then(data=>{
console.log(data)
}).finally(()=>{
console.log("开心...")
Promise.reject("没钱").catch(data=>{
console.log(data)
}).finally(()=>{
console.log("不开心...")
四、promise的使用总结
Promise是一个类,使用之前需要new ,在new的时候,返回的一个promise对象,需要给Promise传递一个执行器。这个执行器会立即执行。
在执行器中有两个参数:resolve,reject,是函数,就可以把promise从等待状到成功或失败。 promise有一个then方法,如果到成功状,它会调用then中的第1个函数 如果失败了,它会调用then中的第2函数。 在then中第1个函数的参数就是成功的结果, 在then中第2个函数就的参数就是失败的结果
大家可以搜下pormise的面试题,坐下参考:其中可能比较重要的要能手写一个简单的promise了。 下面也是我在网上找的面试点,大家共同学习:
- 1、了解 Promise 吗?
- 2、Promise 解决的痛点是什么?
- 3、Promise 解决的痛点还有其他方法可以解决吗?如果有,请列举。
- 4、Promise 如何使用?
- 5、Promise 常用的方法有哪些?它们的作用是什么?
- 6、Promise 在事件循环中的执行过程是怎样的?
- 7、Promise 的业界实现都有哪些?
- 8、能不能手写一个 Promise 的 polyfill。