你的一句承诺,就能避免很多错

你的一句承诺,就能避免很多错

Scroll Down

Promise简介

它允许您将处理程序与异步操作的最终成功值或失败原因相关联。这使异步方法可以像同步方法一样返回值:异步方法返回立即提供最终值的承诺,而不是立即返回最终值。

为什么用Promise

因为JavaScript是单线程的,一些I/O操作,如网络请求,读写文件等等会使得进程阻塞,使人看起来是无响应状态,但异步操作可以弥补,可是异步操作在某些场景会使得我们得到的结果“不一致”,例如:

const fs = require('fs');
fs.readFile('./cookie/bilibili.txt', (err, data)=>{
    if (err) throw "error";
    console.log(data); 
});

console.log("假如此处是需要等待读取文件完成后才要执行的操作");

结果:

readfile.png

当然一般读一些必要文件(如cookie、配置文件)之类的,用同步可能更好,这里只是做异步操作演示,这使得我们的代码看起来好像不是按照“顺序结构”执行,直接这么用,就会出问题。
异步不会马上返回结果,他会在将来的某个时刻执行完毕后才返回结果,如果按照顺序结构来实现逻辑,必然会出现问题,
在许多时候我们需要嵌套来解决问题,当使用异步回调来嵌套时,一不小心就会踏入回调地狱,使得代码看起来臃肿,难以维护

用Pormise来解决有什么好处

  • promise是一个对象,对象和函数的区别就是对象可以保存状态,函数不可以(闭包除外)
  • 代码易读,便于维护
  • 多个异步等待合并便于解决
  • 并未剥夺函数return的能力,因此无需层层传递callback,进行回调获取数据,无论成功与否,都会将结果与状态联系起来

Promise三大状态

  1. 初始状态(pending)
  2. 成功状态(fulfilled)
  3. 失败状态(rejected)

只能由初始变为成功或失败状态,状态一经改变不会再变,Promise是一个构造函数,自身有all、reject、resolve等方法,原型上有then、catch等方法。
resolve:作为Promise的两个参数之一,与异步操作成功相关联,并将成功结果作为参数返回
reject:作为Promise的两个参数之一,与异步操作失败相关联,并将失败结果作为参数返回
resolveandreject.png
all:方法执行后返回的依旧是promise,可以并行多个异步操作,都成功就返回成功,并且在一个回调中处理所有的返回数据。返回的数据与传的参数数组的顺序是一样的。

function read(content) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve(content)
        }, 1000)
    })
}

let result = Promise.all([read(1), read(2)]);
result.then((data) => {
    console.log(data) //[ 1, 2 ]
})

then用于捕获状态的改变,当有多个then的时候,如果then中返回了一个promise 会将promise的结果继续传给第二then中(如果结果是将状态改成成功就走下一个then的成功回调,状态改为失败就走下一个then的失败回调)
这个部分可参照简书作者新叶子的文章Promise的基本用法(一),我就不一一搬过来了

catch用于捕捉异常,当然单个的情况,也可以将异步操作失败的结果,作为参数放到catch中,与放到then的第二个参数基本一致,当有多个的时候,catch用于公共的error

实例场景

这是我自己写一个接口时遇到的场景。
需求:
首先手动获取cookie,存入文件,当用户访问我的接口时,读取并携带cookie,对用户传参进行处理请求第三方接口,获取数据。此处cookie有效期为两小时,需要服务端在请求第三方接口获取数据时,如果检测到cookie过期,去请求refresh接口刷新cookie,先用刷新的cookie去请求第三方接口返回成功数据给用户后,在更新本地cookie文件,
这其中涉及了两次异步网络请求、一次异步写文件操作,第一次写的时候,没想那么多,直接多次回调,后来便出现了问题,异步网络请求还未完成,代码已经执行到了结尾,重启进程才能继续使用,

初步解决方案:
请求第三方接口作为单独一个main函数,刷新token作为单独refresh_token函数,只需要在检测到cookie过期时调用refresh_token这个函数,成功后回调main函数,后来发现跟初始方案差不多,依然会出问题,想到了当初写领券小程序时候用到的Promise解决生成淘口令并提示用户的问题。

最终解决方案:
main函数和refresh_token函数均返回一个Promise,将成功和失败逻辑处理好,这样一来,在最后返回结果处就一定能保证返回正确结果(这里是第三方接口未失效、网络等一切正常),至于写文件操作依然用异步,这样能以最快的速度将结果返回给用户,而写文件操作继续执行中

个人愚见,部分来源于网上资料,如有错误请指正!!!