本文已参与「新人创作礼」活动,一同敞开创作之路。

知道Promise目标的你,知道在运用Promise目标的时候,咱们怎么做到在运用中如虎添翼嘛?10分钟带你迅速上手!

一、Promise的诞生
1、回调阴间

开始javascript的异步完成便是运用回调函数。回调阴间便是:一个函数需求等它的回调函数(或许回调和回调的回调…)履行完毕之后再履行。简略来说,回调函数里边嵌套回调函数。而由于回调阴间的问题,Promise就出现了。咱们看看什么是回调阴间:

// 回调阴间
//阴间回调
setTimeout(function () { //第一层
  console.log(1);//等4秒打印1,然后履行下一个回调函数
  setTimeout(function () { //第二层
    console.log(2);//等3秒打印2,然后履行下一个回调函数
    setTimeout(function () {  //第三层
      console.log(3);//等2秒打印3,然后履行下一个回调函数
      setTimeout(function () {  //第四层
        console.log(4);//等1秒打印4
       }, 1000)
     }, 2000)
   }, 3000)
}, 4000)

可看出回调阴间的特点

1.难以复用
2.仓库信息被断开
3.凭借外层变量

回调阴间是为了让咱们代码履行顺序的一种操作(解决异步),可是它会使咱们的可读性十分差。当你有100个,1000个…,代码会不断右移,不行优雅,也会影响性能。嵌套和缩进仅仅回调阴间的一个梗罢了,它导致的问题远非嵌套导致的可读性下降罢了。接下里咱们今天的意图,便是将上面的回调阴间用Promise解决。

二、Promise的行为
1、Promise的语法

Promise的根本语法:Promise函数接收一个函数作为参数,这个函数有两个参数,一个是成功函数(resolve),一个是失利函数(reject)。Promise的.then接收两个回调函数,一个是成功函数的回调,一个是失利函数的回调。这两个函数可选,不一定要供给

//Promise语法
let myPromise = new Promise(function(resolve, reject) {
  // "Producing Code"(或许需求一些时刻)
  
   resolve(); // 成功时
   reject(); // 犯错时
   });
  
  // "Consuming Code" (有必要等待一个实现的承诺)
  myPromise.then(
   function(value) { /* 成功时的代码 */ },
   function(error) { /* 犯错时的代码 */ }
   );

特别注意:

(1)Promise目标中的状况不会被外界干扰。状况的改动取决于异步的操作成果。

(2)Promise目标的状况一旦被改动,就不会进行再次改动。
例如:

let myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok');//第一次状况为成功
    reject('no');//不会改动
   })
}).then(
  function (result) { console.log('resolved'); },//成功状况履行then后边的成功回调函数
  function (error) { console.log('reject'); }
)
//resolved

(3)Promise新建后就会当即履行,Promise后边的.then是一个异步操作,在事情循环中叫做“微使命”。会放在同步代码后边履行。
例如:

let myPromise=new Promise((resolve,reject)=>{
  console.log('Promise');//1
  resolve();
})
   .then(()=>{//这儿是一个异步操作
    console.log('succeed');//3
   })
console.log('Promise resolved');//2
// Promise
// Promise resolved
// succeed
2、Promise的办法
(1)Promise.prototype.then()

then办法的回来成果是新的Promise实例,目标状况由回调函数的履行成果决议。then办法后边还能够再调用另一个then办法,构成链条。选用链式的then,能够指定一组依照次第调用的回调函数。

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    //设置 p 目标的状况为失利,并设置失利的值 
    reject("犯错啦!");
   }, 1000)
});
p.then(
  function(value){},
  function(reason){console.log(reason);}
)
.then(()=>{
  console.log(123)
});
(2)Promise.prototype.catch()

catch()用于指定发生过错时的回调函数.例如:

const p = new Promise((resolve, reject) => {//p为Promise的实例
  setTimeout(() => {
    //设置 p 目标的状况为失利,并设置失利的值 
    reject("犯错啦!");//reject()办法的作用,等同于抛犯过错
   }, 1000)
});
// p.then(function(value){},function(reason){
// // console.error(reason); 
// console.log(reason);
// });
​
p.catch(function (reason) {//相当于上面的.then(...)
  console.log(reason);//捕获reject状况的值
});

主张总是运用catch()办法,而不运用then()办法的第二个参数.

catch()还能够这样运用:

const myPromise = new Promise(function(resolve, reject) {
  throw new Error('犯错啦');//从这抛犯过错,catch()指定的回调函数也能够捕获
  });
 promise.catch(function(error) {
  console.log(error);
});
(3)Promise.prototype.finally()

finally()办法用于指定不管 Promise 目标最终状况怎么,都会履行的操作。并且finally办法总是会回来本来的值。举个比如:

function a(){
  return new Promise((resolve,reject)=>{
    resolve('ok')
   },1000)
}
​
function b(){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      reject('no')
     },1500)
   })
}
function c(){
  setTimeout(()=>{
     console.log('finally');
   },500)
  
}
a()
   .then((res)=>{
    console.log(res);
    return b()
   })
   .catch((err)=>{
    console.log(err);
   })
   .finally(c)//参数不是回调函数,假如是回调函数也不需求带参//ok
//no
//finally //阐明finally()照样履行,有打印
(4)Promise.resolve()

将现有目标转为 Promise 目标,状况为resolved。举例如下:

let myString='hello';
console.log(myString);//hello
const myPromise=Promise.resolve(myString)//带参
//等同于const myPromise=Promise.resolve('hello')
console.log(myPromise);//Promise { 'hello' }
myString=Promise.resolve();//不带参,直接调用
console.log(myString);//Promise { undefined }
myString.then(result=>{//阐明myString已经是Promise目标了,只有Promise目标才有.then
  console.log(result);//undefined
})
(5)Promise.reject()

也会回来一个新的 Promise 实例,该实例的状况为rejected。简略举例:

// 以上代码等于
const p=new Promise((resolve,reject)=>{
  reject('error')
})
p.catch(error=>{
  console.log(error);//error
})
// 或许
p.then(function(){},function(error){console.log(error);})//error
// 或许
p.then(null,function(error){console.log(error);})//error
// 或许
p.then(undefined,function(error){console.log(error);})//error
(6)Promise.all()

all()是将多个 Promise 实例,包装成一个新的 Promise 实例。接收一个数组作为参数,数组的每一项都是Promise目标的实例。假如不是,会通过Promise.resolve()将参数转为Promise实例,再进行处理。all()用于将多个 Promise 实例,包装成一个新的 Promise 实例

// promise.all()
const myPromise1=new Promise((resolve,reject)=>{
  resolve('sure');
})
   .then(result=>result)
const myPromise2=new Promise((resolve,reject)=>{
  reject('cancel')
})
   .then(result=>result)
   .catch(error=>error)//myPromise2有自己的catch
//感兴趣的小伙伴能够尝试,假如删除myPromise2.catch(...)后Promise.all([myPromise1,myPromise2])会怎么?
Promise.all([myPromise1,myPromise2])//myPromise1,myPromise2都处于成功状况
   .then(result=>{console.log(result);})//走这儿  [ 'sure', 'cancel' ]
   .catch(error=>{console.log(error);})
(7)Promise.race()

race()是将多个 Promise 实例,包装成一个新的 Promise 实例。接收一个数组作为参数,数组的每一项都是Promise目标的实例。假如不是,会通过promise.resolve()将参数转为Promise实例,再进行处理。只要参数的Promise实例有一个首先改动状况,则状况改动。例如:

const myPromise2 = new Promise((resolve, reject) => {
  setTimeout(()=>{
    reject('cancel') 
   },3000)
  //假如将时刻改为<2000,Promise.race([myPromise1, myPromise2])走哪一步呢?
  
})
   .then(result => result)
   .catch(error => error)
const myPromise1 = new Promise((resolve, reject) => {
  setTimeout(()=>{
    resolve('sure');
   },2000)//myPromise1比myPromise2更先改动状况
  
})
   .then(result => result)
​
​
Promise.race([myPromise1, myPromise2])
   .then(result => { console.log(result); })//走这儿,sure
   .catch(error => { console.log(error); })

简要说一下const p1=Promise.all([promise1,promise2,promise3]) 和const p2=Promise.race([promise1,promise2,promise3])的区别

–前者Promise的实例状况都为resolved时,p1调用.then()里边成功时的回调函数;假如实例状况有一个为rejected,p1调用.then()里边失利时的函数或许走.catch()

–后者注重时序,假如首先改动状况的实例为resolved,则p2为reslove状况;否则,为reject状况。

三、Promise的场景
1、Ajax请求
<head>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
   <div class="name">
      <audio id="audio" controls></audio>
   </div>
   <script>
      function getSong() {
       return new Promise((resolve, reject) => {
       $.ajax({
        url: 'https://www.fastmock.site/mock/c024e8920dd6003c63dcd9ed2bbf6cb9/music/music',
        dataType: 'json',
        success(res) {
            console.log(res);
            url = res[0].url;
        }
    })
      resolve();
    })
  }
            function playSong() {
                let audio = document.getElementById('audio');
                window.addEventListener('click', function () {
                audio.src = url;
                audio.play()
            })
            }
            getSong().then(playSong())
    </script>
</body>
2、文件读取
// 引进fs模块
const fs=require('fs');
// 运用Promise封装
const P=new Promise(function(resolve,reject){
    fs.readFile('./text/2.md',(err,data)=>{
        // 假如地址过错,抛出反常
        if(err) reject(err) ;
        // 假如成功,输出内容
        resolve(data);
    });
});
P.then(function(value){
    console.log(value.toString());
},function(reason){
    console.log("defeat!!!!");
});
3、图片加载
<body>
    <img src="http://m.imeitou.com/uploads/allimg/220514/5-220514101036.jpg" alt="景色" id="myImage">
    <script>
        const preloadImage = function (path) {
            return new Promise(function (resolve, reject) {
                const image = new Image();
                // 图片加载成功
                image.onload=()=>{
                    resolve(image)
                }
                // 图片加载失利
                image.onerror=()=>{
                    reject('sorry,cannot loding picture')
                };
                image.src = path;
            });
        };
        // 获取图片DOM节点
        var preImage=document.getElementById('myImage')
        // 图片预加载
        preloadImage('https://www.6hu.cc/files/2022/12/1670492847-af9a1be924976f9.jpg')
        .then(targetImage=>
            // 点击页面切换图片,让图片加载
            window.onclick=function(){
                setTimeout(()=>{
                       preImage.src=targetImage.src
                },1000)
            }
        )
    </script>
</body>
4、函数封装
//例如将微信小程序里的showModal进行Promise封装,那么在任何需求用到此函数的直接引进就很方便了
// promise形式 封装 showModal
export const showModal=({content})=>{
    return new Promise((resolve,reject)=>{
        wx.showModal({
          title:'提示',
          content:content,
            success: (result) => {
                resolve(result);
            },
            fail: (err) => {
                reject(err);
            }
        });
    })
}
四、Promise的短板

1.无法撤销Promise,一旦新建它就会当即履行,无法中途撤销

2.假如不设置回调函数,Promise内部抛出的过错,不会反映到外部

3.当处于pending状况时,无法得知现在发展到哪一个阶段,是刚刚开始仍是即将完成

总结

恭喜你看到文章结尾了,相信你能够写出Promise方案解决第一个回调阴间的异步问题了,快去试试把!