通俗易懂之Promise!

311 阅读6分钟

都知道javaScript代码是单线程执行的,就是这个原因,导致了JavaScript中所有的网络操作,浏览器事件,都必须满足异步执行的要求,此时Promise就诞生了。简单来说Promise是一种处理异步请求的解决方案。

初始Promise

Promise三种状态

Promise 是一个构造函数,需要使用new关键字生成实例

let p = new Promise(() => {

})
console.log(p);

这里写了一个简单的Promise,输出如下结果:

可以看出在Promise未指定任何状态时,返回的状态为pending(等待状态), 还有两段代码:

let p = new Promise((resolve, reject) => {
  resolve('ok')
})
console.log(p);
let p = new Promise((resolve, reject) => {
  reject('err')
})
console.log(p);

两段代码的结果分别为:

不难看出,Promise的构造函数接收一个参数:函数,并且这个函数需要传入两个参数:

  • resolve :异步操作执行成功后的回调函数,指定了成功的状态为fulfiled(成功状态), 且成功状态的值为你设置的ok
  • reject:异步操作执行失败后的回调函数,指定了失败的状态为rejected(失败状态), 且失败状态的值为你设置的err

注:失败的回调这里控制台有报错信息,这里不是我们的代码错误,是Promise 的失败回调函数抛出了一个错误。

Promise状态不能被改变

有这么一段代码:

let p = new Promise((resolve, reject) => {
  resolve('ok')
  reject('err')
})
console.log(p);

此时结果:

由此得出Promise状态一旦改变就不会再变了,创造Promise实例后它会立即执行

.then

.then的链式调用

有这么一个需求:

请求三次数据,请求第一次的参数为params=321,而后两次参数分别为前一次返回的结果?

普通方式解决如下:

getData('/api/a/1?params=321',(res1) => {
  console.log(res1);
  getData(`/api/b/2?params=${res1.data.params}`, (res2) => {
    console.log(res2);
    getData(`/api/c/3?params=${res2.data.params}`, (res3) => {
      console.log(res3);
    })
  })
})

以上这种方式就是传说中的回调地狱--> 回调里面套回调。

此时我们使用Promise来优化上述代码:

getData('/api/a/1?params=321').then((res1)=>{
  console.log(res1)
  return getData(`/api/b/2?params=${res1.data.params}`
}).then((res2)=>{
  console.log(res2)
  return getData(`/api/c/3?params=${res2.data.params}`
}).then((res3)=>{
  //得到最终结果
   console.log(res3)
})

被优化后的代码是通过.then解决后的结果,也是成功的解决了回调地狱的问题。

又有两段代码:

let p = new Promise((resolve, reject) => {
  resolve('ok')
})

let result = p.then((res) => {
  return 'ok'
})
console.log(result);

得到结果为

let p = new Promise((resolve, reject) => {
  resolve('ok')
})

let result = p.then((res) => {
  return new Promise((resolve, reject) => {
    reject('err')
  })
})
console.log(result);

结果为:

因为链式调用的结果上述代码中的p.then也是一个Promise,由此得出:

如果Promise的then方法的成功或失败返回是非Promise,那么then方法返回的Promise实例就为成功的,值为return的那个非Promise值,如果返回的是Promise,那么then方法返回的Promise实例就取决于上个Promise的返回结果

.then的两个参数

.then可以接收两个参数,并且两个参数都是回调函数, 有这么一段代码:

let p = new Promise((resolve, reject) => {
  let name = '张学友'
  if (name === '刘德华') {
    resolve('这是刘德华')
  } else if (name === '张学友') {
    resolve('这是张学友')
  } else {
    reject('啥也不是')
  }
})
p.then((res) => {
  console.log('成功',res);
}, (err) => {
  console.log('失败', err);
})

上述代码当你随意切换name的值会发现,你使用resolve指定为成功的结果时,then方法就会执行第一个成功回调, 若你使用reject指定为失败的结果时,then方法就会执行第二个失败回调。

.catch

首先我来演示一个错误:

let p = new Promise((resolve, reject) => {
  resolve('ok')
})
p.then((res) => {
  console.log(res);
  console.log(a);
},(err)=>{
  console.log(err);
})

如上述代码所示,我定义了一个为定义的变量a,运行结果如下图:

可以看出报错终止了代码的运行,,错误回调并没捕获错误的结果。 此时我换成catch:

let p = new Promise((resolve, reject) => {
  resolve('ok')
})
p.then((res) => {
  console.log(res);
  console.log(a);
}).catch((err)=>{
  console.log(err);
})

结果为:

也就是说进入catch中时,把错误原因传到参数中,即便有错误代码也不会报错了,与try/catch相似。

Promise.all()

Promise下的all方法接受一个由多个Promise组成的数组,所有Promise结果成功,才返回成功 的Promise回调, 上代码:

 function getLunbo() {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('请求轮播图成功')
                }, 1000)
            })
        }
        function getTab() {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('请求tab栏成功成功')
                }, 2200)
            })
        }
        function getLayOut() {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('请求侧边栏成功')
                }, 3000)
            })
        }
        function getPic() {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    reject('请求图片成功')
                }, 1200)
            })
        }
        let a = getLunbo()
        let b = getTab()
        let c = getLayOut()
        let d = getPic()
        let all = Promise.all([a, b, c, d]).then((value) => {
            console.log(value);
            console.log('请求所有数据成功');
        })

结果如下

如果全部成功,就返回所有成功结果组成的数组,如果其中一个Promise返回失败的结果则:

Promise.any()

Promise下的any方法也接受一个由多个Promise组成的数组 一个Promise返回结果为成功,整体就返回成功的promise,所有的都失败才返回失败的promise。

   let x1 = new Promise((resolve, reject) => {
            resolve('ok')
        })
        let x2 = new Promise((resolve, reject) => {
            reject('err')
        })
        let x3 = new Promise((resolve, reject) => {
            reject('err1')
        })
        let any = Promise.any([x1, x2, x3]).then((value) => {
            console.log(value);
        })
        console.log(any);

结果为

如果都是失败的Promise则:

Promise.race()

race 赛跑的意思,以第一个 有结果的promise为主,成功即为成功,失败即为失败

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 1000)
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(2);
  }, 3000)
})
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(3);
  }, 5000)
})
const result = Promise.race([p3, p1, p2]);
console.log(result);

上述代码执行最快的是第一个,所以Promise.race([p3, p1, p2]);的结果取决于第一个,第一个为pending,result的结果就是pending,为成功就成功,为失败就失败。

Promise.finlly()

const p1 = new Promise((resolve, reject) => {
  //resolve('ok');
  reject('error');
});
p1.then(value => {
  console.log(value);
}).catch(reason => {
  console.log(reason);
}).finally(() => {
  console.log('最终我被执行了...');
})

不难看出,不管成功与否,失败与否都会执行finally(), 且finally回调函数不接收参数

Promise.allSettled()

当所有的异步操作都有结果时,包装实例才结束,返回成功的Promise, 目前我未曾用过这个方法,后续我将补充

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

let result = Promise.allSettled(promises).then((results) => results.forEach((result) => console.log(result.status)));
//返回成功的Promise
console.log(result);

上述代码摘自MDN

Promise.resolve()

当Promise.resolve()中传入了非Promise,那么包裹对象返回成功的Promise,成功的结果为非Promise的,如果传入的是Promise,那么包裹对象返回的结果为Promise返回的结果

Promise.reject()

直接指定为失败状态的Promise,返回一个带有拒绝原因reason参数的Promise对象。

anync与await

await 也是一个修饰符,只能放在async定义的函数内。可以理解为等待等待成功的回调。

await必须写在async函数中,但是async函数中可以没有await

如果await的promise失败了,就会抛出异常,需要通过try...catch捕获处理

async function getData () {
  try{
    //成功
   let res1 = await getDataList('/api/a/1?params=123'); 
  }catch(err){
    //失败
      console.log(err.message)
  }
}

上述代码anync与await的基本使用,后续还会补充。

最后

关于Promise的总结:

1.Promise可以解决代码的回调地狱(回调里面套回调),从而简化代码

2.Promise可以解决异步的问题,但本身不能说Promise是异步的

3.anync与await是开发主流,可通过try...catch捕获异常

这是我的个人博客:XieJinYang的博客 (gitee.io)

希望大家有所收获!!!!!!!!!!!!!!!!!!!!!!!!

不是吧震惊表情包_爱给网_aigei_com.gif

猫meme-啥?_爱给网_aigei_com.gif