这样写的promise 就很容易懂

428 阅读6分钟

一、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。

参考链接:juejin.cn/post/684490…

juejin.cn/post/684490…