Promise

作者: 未路过 | 来源:发表于2022-09-17 22:41 被阅读0次

1 异步事件的处理

那什么时候我们会来处理异步事件呢?
一种很常见的场景应该就是网络请求了。
我们封装一个网络请求的函数,因为不能立即拿到结果,所以不能像简单的3+4=7一样将结果返回。
所以往往我们会传入另外一个函数,在数据请求成功时,将数据通过传入的函数回调出去。

  //1.
  // request.js
function requestDate(url){
  // 模拟网络请求
  setTimeout(() =>{
       // 拿到请求的结果
    // url传入的是coderwhy, 请求成功
    if(url == "coderwhy"){
      //成功
      let name = ["abc", "cba", "nba"];
    }else{
      let errMessage = "请求失败, url错误";
    }
  })
}

// main.js
requestDate("kobe")
//怎么让main.js这个文件得到网络请求name或者errMessage的结果?

 //2.
function requestDate(url){
  // 模拟网络请求
  setTimeout(() =>{
       // 拿到请求的结果
    // url传入的是coderwhy, 请求成功
    if(url == "coderwhy"){
      //成功
      let name = ["abc", "cba", "nba"];

      return name
    }else{
      let errMessage = "请求失败, url错误";
      return errMessage
    }
  })
}

// main.js
let result = requestDate("kobe")

//这样子时拿不到返回结果的,因为网络请求时异步的,拿到的至于requestDate的返回结果
//return name时setTimeOut的rutrun结果,是拿不到的


 //3.
//解决方法:传入对应的回调函数
// request.js
function requestData(url, successCallback, failtureCallback) {
  // 模拟网络请求
  setTimeout(() => {
    // 拿到请求的结果
    // url传入的是coderwhy, 请求成功
    if (url === "coderwhy") {
      // 成功
      let names = ["abc", "cba", "nba"]
      successCallback(names)
    } else { // 否则请求失败
      // 失败
      let errMessage = "请求失败, url错误"
      failtureCallback(errMessage)
    }
  }, 3000);
}


// main.js
requestData("kobe", (res) => {
  console.log(res);
  console.log("发送请求成功")
  .....很长很长代码
}, (err) => {
  console.log(err)
})

// 更规范/更好的方案 Promise承诺(规范好了所有的代码编写逻辑)
function requestData2() {
  return "承诺"
}

const chengnuo = requestData2()

* 这种回调的方式有很多的弊端:

* 1> 如果是我们自己封装的requestData,那么我们在封装的时候必须要自己设计好callback名称, 并且使用好

如果只是一个简单的网络请求,那么这种方案不会给我们带来很大的麻烦。

但是,当网络请求非常复杂时,就会出现回调地狱。

2 网络请求的回调地狱

151.PNG

回调地狱,举个简单的例子,就是让你1s之后打印一个1,再2s之后打印一个2, 再3s之后打印一个3

  setTimeout( () => {
    console.log(1);
    setTimeout( () => {
      console.log(2);
      setTimeout( () => {
        console.log(3);
      },3000)
    }, 2000)
  }, 1000)

如果这里不仅仅是打印,而是很长的逻辑的话,就会嵌套,很容易出问题。

3 Promise的介绍

117.PNG

Proxy是es6里面新增的api

function foo(){
  return "承诺"
}

const chengnuo = foo()
//当我们调用foo的时候,foo不能马上给我们一个结果的时候,
//我们希望它给我们返回一个"承诺"
//在这里这个"承诺"就是Promise 


//相当于下面代码
function foo(){
  return new Promise()
}

const promise = foo()

//相当于

const promise = new Promise()

function Person(name, age){

}

const p = new Person("why", 18)

//像是p实例一样,promise里面也可以传入参数,其中参数是一个回调函数
//并且这个回调函数一传进去就会立刻执行
//传入的这个函数被称为Executor
const promise = new Promise(() => {
  console.log("传入的函数被执行了");
})

/* 
像是下面一样
class Promise{
  constructor(callback){
    callback()

  }
} 
*/

//而且executor执行的时候会给你再传入一些其他的参数

class PromiseTest{
  constructor(callback){
    let foo = function(){ 

    };
    let bar = function(){

     };
    callback(foo, bar)

  }
} 

const promiseTest = new PromiseTest(() => {
  console.log("传入的函数被执行了");

})

//实际promise里面,这里的foo和bar是resolve和reject
const promise = new Promise((resolve, reject) => {
  console.log("传入的函数被执行了");

})

//上面例子中的foo函数
function foo() {
  // Promise
  return new Promise((resolve, reject) => {
    resolve("success message")
    // reject("failture message")
  })
}

const fooPromise = foo()

//返回promise之后,就是别人拿到promise之后,我们要做一些网络请求的东西
//成功之后给我成功的结果,失败给我失败的结果
//如果成功的话,给我调别人给我的resolve函数
//失败的话,给我调别人给我的reject函数


/* 

在这里fooPromise只是一份承诺,不是一个结果
需要调用fooPromise的then方法
fooPromise.then(),并且 可以往里面再传入一个回调函数
在里面调用resolve方法的时候,外面的fooPromise.then()方法就会被自动执行
我们就知道这次做了一个成功的回调 

里面调用reject的话,外面会自动调用fooPromise.catch()
*/

// promise.then(() => {

// })

// promise.catch(() => {

// })

4. 异步请求的Promise


  //request.js
  function requestData(url) {
    // 异步请求的代码会被放入到executor中
    return new Promise((resolve, reject) => {
      // 模拟网络请求
      setTimeout(() => {
        // 拿到请求的结果
        // url传入的是coderwhy, 请求成功
        if (url === "coderwhy"){
          //成功
          let names = ["abc", "cba", "nba"];
          resolve(names)
            //这个时候回调函数就判断数据拿到了没有,所以说promise返回的是一个承诺,处理成功或者失败的承诺,后续的操作不再这里搞
            //,接下来的很长很长处理数据的代码就放到then里面
            //以前的处理方式是数据拿到的话,就执行回调函数,马上执行一大段代码,现在是判断和代码分开了,因为promise给了我们承诺
        } else {
          let errMessage = "请求失败, url错误";
          reject(errMessage);
        }
      }, 3000)
    } )
  }

  // main.js
  const promise = requestData("coderwhy");
/*   promise.then((res) => {
    console.log("请求成功:", res)
  })
  promise.catch((err) => {
    console.log("请求失败:", err)
  }) */

  /* 
  
  then和catch分开写会在node中报异常,
  还有一直写法是在then里面直接传两个回调函数
  一个是成功时候的回调,一个是失败时候的回调

 then方法传入的回调函数两个回调函数:
 第一个回调函数, 会在Promise执行resolve函数时, 被回调
 第二个回调函数, 会在Promise执行reject函数时, 被回调
  */

 promise.then((res) => {
    console.log("请求成功:", res)
  }, (err) => {
    console.log("请求失败:", err)
  })

5. Promise的三种状态

118.PNG

pending
adj. 悬而未决的; 待定; 待决; 即将发生的;

一旦调用resolve(),就是fulfilled状态,调用reject,就是rejected状态

// const promise = new Promise((resolve, reject) => {

// })

// promise.then(res => {

// }, err => {

// })

// 完全等价于下面的代码
// 注意: Promise状态一旦确定下来, 那么就是不可更改的(锁定)
new Promise((resolve, reject) => {
  // pending状态: 待定/悬而未决的
  console.log("--------")
  reject() // 处于rejected状态(已拒绝状态)
  resolve() // 处于fulfilled状态(已敲定/兑现状态)
  console.log("++++++++++++")
}).then(res => {
  console.log("res:", res)
}, err => {
  console.log("err:", err)
})

4 Promise的resolve参数

* resolve(参数)有三种情况

  1. 普通的值或者对象 pending -> fulfilled*

  2. 传入一个Promise, 那么当前的Promise的状态会由传入的Promise来决定, 相当于状态进行了移交

 const newPromise = new Promise((resolve, reject) => {
   resolve("aaaa")
 })

 new Promise((resolve, reject) => {
   //状态本来应该是pending -> fulfilled
   //但是因为传入的参数是promise,相当于状态进行了移交,
   //当前的状态由新的promise决定,现在还是pending,直到新的promise调用了reslove或者reject
   //新的promise执行力resolve("aaaa")
   //下面的.then里面的东西被自动执行,参数是新的promise里面传来的东西
   resolve(newPromise)
 }).then(res => {
   console.log("res:", res);
 }, err => {
   console.log("err", err);
 })
  1. 传入一个对象, 并且这个对象有实现then方法(并且这个对象是实现了thenable接口), 那么也会执行该then方法, 并且由该then方法决定后续状态
//传入一个对象, 这个兑现有then方法
new Promise((resolve, reject) => {
   const obj = {
    then: function(resolve, reject){
      resolve("resolve message")
    }
  } 
  resolve(obj)
   
}).then(res => {
  console.log("res:", res);
}, err => {
  cconsole.log("err:", err);
})


// eatable/runable
const obj = {
  eat: function() {

  },
  run: function() {

  }
}

5 Promise的对象方法then

  • then是promise的对象方法,意味这要使用的话,首先得new一个对象,然后调用这个方法。如果是通过类名Promise直接调用一个方法,比如all方法,Promise.all(), 就是类方法,就是不用创建出新对象去访问的方法。也就是类方法是静态方法, 对象方法是在原型上的方法。

  • then方法是Promise对象上的一个方法:它其实是放在Promise的原型上的 Promise.prototype.then

 // Promise有哪些对象方法
  //console.log(Promise.prototype);
  //console.log(Object.getOwnPropertyDescriptors(Promise.prototype))
  //直接答应不出来是因为这几个属性的enumerable: false


  class Person {
    constructor(executor) {
      const resolve = () => {
        this.callback();

      }
      const reject = () => {

      }

      executor(resolve, reject)
    }

    then(callback) {
      this.callback = callback
    }


  }

  const promise = new Promise((resolve, reject) => {
    resolve("hahaha")
  })

  // 1.同一个Promise可以被多次调用then方法
  // 当我们的resolve方法被回调时, 所有的then方法传入的回调函数都会被调用
  promise.then(res => {
    console.log("res1:", res)
  })

  promise.then(res => {
    console.log("res2:", res)
  })

  promise.then(res => {
    console.log("res3:", res)
  })


  // 2.then方法传入的 "回调函数: 可以有返回值
// then方法本身也是有返回值的, 它的返回值是Promise


// 1> 如果我们返回的是一个普通值(数值/字符串/普通对象/undefined), 那么这个普通的值被作为一个新的Promise的resolve值
promise.then(res => {
    return "aaaaa"
  })


  //以上代码相当于

promise.then(res => {
  return new Promise((resolve) => {
    resolve("aaaaa")

  })
})

//因此,接下来在then的话,对应的就是上一个then return的新的promise
promise.then(res => {
    return "aaaaa"
  }).then(res => {
    console.log("res:", res);
    return "bbbbb"
  })

  //如果第一个return没有写ruturn, 默认返回的是undefined


  promise.then(res => {
    return undefined
  }).then(res => {
    console.log("res:", res);
    return "bbbbb"
  })

// 2> 如果我们返回的是一个Promise

promise.then(res => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(111111)
    }, 3000)
  })
}).then(res => {
  console.log("res", res);
})

/* 
在这里看,是第一个then返回了一个promise
其实是一个看不见的promise包裹着这里写着的promise

return new Promise((resolve, reject) => {
  resolve(
     new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(111111)
    }, 3000)
  })
  )
})

根据上一节内容,resolve里面有一个新的promise的话,
当前的状态由新的promise决定,现在还是pending,直到新的promise调用了reslove或者reject
所以三秒之后会转为fulfilled,然后执行最后的
(res => {
  console.log("res", res);
})

也就是3之后打印111111
*/

// 3> 如果返回的是一个对象, 并且该对象实现了thenable
promise.then(res => {
  return {
    then: function(resolve, reject) {
      resolve(222222)
    }
  }
}).then(res => {
  console.log("res:", res)
})

/* 
promise.then(res => {
  return {
    new Promise(resolve, reject) => {
      resolve(
        then: function(resolve, reject) {
        resolve(222222)
    }
      )
    }
  }
}).then(res => {
  console.log("res:", res)
})


根据上一节内容,resolve里面有一个对象, 并且这个对象有实现then方法(并且这个对象是实现了thenable接口),
 那么也会执行该then方法, 并且又该then方法决定后续状态

 打印结果是2222
*/

6 Promise的对象方法catch

 //const promise = new Promise((resolve, reject) => {
  //  resolve('success')
    //方法1:
    //reject("rejected status")
    //方法2:也会调用then的第二个参数(rejecte的回调函数)
    //throw new Error("rejected status")
//  })

  // 1.当executor抛出异常时, 也是会调用错误(拒绝)捕获的回调函数的
/*   promise.then(undefined, err => {
    console.log("err:", err);
  }) */

//2.then的第一个参数是回调函数,第二个也是,看起来很容易出问题,阅读性比较不好,分开来比较好
//通过catch方法来传入错误(拒绝)捕获的回调函数,是上面正规方法的语法糖
//不符合promise/a+规范
/* promise.catch(err => {
  console.log("err:", err);
}) */

//下面的情况
//这个catch是优先第一个promise的异常捕获,而不是return的新的promise
//如果第一个promise没有异常的话,会留着给下一个用return的新的promise

/* promise.then( res => {
  console.log("res:", res);
  return '11111'
}).catch(err => {
  console.log("err:", err);
}) */
//上面第一个promise是reject("rejected status"),因此会回调
//catch里面的东西,


/* 
如果把第1个promise设置成resolve,看catch会不会捕获第二个promise

*/
/*   promise.then( res => {
    console.log("res:", res);
    return new Promise( (resolve, reject) => {
      reject('the second promise err status')
    })
  }).catch( err => {
    console.log("err:", err);
  }) */

/* 
结果是回打印出来err: the second promise err status
*/


// 3.拒绝捕获的问题(前面课程)
const promise = new Promise((resolve, reject) => {
  reject("rejected status")
})
//单独写promise.catch的时候就会出现问题
promise.then( res => {
  console.log("res", res);
})

/* 如果没有写reject调用时候的处理,也就是回调函数,会报错 */
/* promise.catch( err => {
  console.log("err:", err);
})

所以要么按正规写法,then里面放两个回调函数,要么在then之后再调用
catch
 */


 //4.catch方法的返回值
 //事实上catch方法也是会返回一个Promise对象的,
 //所以catch方法后面我们可以继续调用then方法或者catch方法:
 //返回的值也是放在新的promise的resolve()作为参数
 //所以.catch{return }之后调用的一定是then
 const promise = new Promise((resolve, reject) => {
  reject("111111")
})
promise.then(res => {
  console.log("res:", res)
}).catch(err => {
  console.log("err:", err)
  return "catch return value"
}).then(res => {
  console.log("res result:", res)
}).catch(err => {
  console.log("err result:", err)
})
//err: 111111
//res result: catch return value

//如果想让catch后调用下一个catch,需要throw一个error
promise.then(res => {
  console.log("res:", res)
}).catch(err => {
  console.log("err:", err)
  throw "catch return value"
}).then(res => {
  console.log("res result:", res)
}).catch(err => {
  console.log("err result:", err)
})
//err: 111111
//err result: catch return value

promise.then(res => {
  console.log("res:", res)
}, err => {
  console.log("err:", err)
  throw "catch return value"
}).then(res => {
  console.log("res result:", res)
}, err => {
  console.log("err2 result:", err)
})
//err: 111111
//err2 result: catch return value

7 Promise的对象方法finally

  • finally是在ES9(ES2018)中新增的一个特性:表示无论Promise对象无论变成fulfilled还是reject状态,最终都会

被执行的代码。

  • finally方法是不接收参数的,因为无论前面是fulfilled状态,还是reject状态,它都会执行。
const promise = new Promise((resolve, reject) => {
  // resolve("resolve message")
  reject("reject message")
})

promise.then(res => {
  console.log("res:", res)
}).catch(err => {
  console.log("err:", err)
}).finally(() => {
  console.log("finally code execute")
})

//err: reject message
//finally code execute

8 Promise类方法resolve

119.PNG
function foo(){
  const obj = { name: "why" }
  // 想把obj转成转成Promise对象,给别人返回出去
  return new Promise((resolve) => {
    resolve(obj)
  })
}
var result = foo();
result.then( res => {
  console.log(res);
})
//{name: 'why'}
//以上的代码我们可以通过Promise.resolve来实现


// 类方法Promise.resolve
// 1.普通的值
 const promise = Promise.resolve({ name: "why" })

// 相当于
// const promise2 = new Promise((resolve, reject) => {
//   resolve({ name: "why" })
// })

promise.then(res => {
  console.log("res:", res)
})

// 2.传入Promise
//和以前一样,旧的promise的状态由新的promise决定
const promise1 = Promise.resolve(new Promise((resolve, reject) => {
  resolve("11111")
}))

promise1.then(res => {
  console.log("res:", res)
})


// 3.传入thenable对象

9 Promise类方法reject

  • reject方法类似于resolve方法,只是会将Promise对象的状态设置为reject状态。

  • Promise.reject的用法相当于new Promise,只是会调用reject:


// const promise = Promise.reject("rejected message")
// 相当于
// const promise2 = new Promsie((resolve, reject) => {
//   reject("rejected message")
// })

// 注意: 无论传入什么值都是一样的
const promise = Promise.reject(new Promise(() => {}))

promise.then(res => {
  console.log("res:", res)
}).catch(err => {
  console.log("err:", err)
})
//err: Promise {<pending>}
  • Promise.reject传入的参数无论是什么形态,都会直接作为reject状态的参数传递到catch的

10 Promise类方法all

  1. 它的作用是将多个Promise包裹在一起形成一个新的Promise;

  2. 新的Promise状态由包裹的所有Promise共同决定:

  • 当所有的Promise状态变成fulfilled状态时,新的Promise状态为fulfilled,并且会将所有Promise的返回值

组成一个数组;

  • 当有一个Promise状态为reject时,新的Promise状态为reject,并且会将第一个reject的返回值作为参数;
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(11111)
  }, 1000);
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(22222)
  }, 2000);
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(33333)
  }, 3000);
})

// 需求: 所有的Promise都变成fulfilled时, 再拿到结果

Promise.all([p2, p1, p3, "aaaa"]).then(res => {
  console.log(res)
}).catch(err => {
  console.log("err:", err)
})
//(4) [22222, 11111, 33333, 'aaaa']
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(11111)
  }, 1000);
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(22222)
  }, 2000);
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(33333)
  }, 3000);
})

// 意外: 在拿到所有结果之前, 有一个promise变成了rejected, 那么整个promise是rejected
//不会再执行then里面的回调了,而是执行catch里面的回调函数
Promise.all([p2, p1, p3, "aaaa"])
    .then(res => {
  console.log(res)
}).catch(err => {
  console.log("err:", err)
})
//err: 22222

11 Promise类方法allSettled方法

120.PNG
// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
    
    
  setTimeout(() => {
    resolve(11111)
  }, 1000);
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(22222)
  }, 2000);
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(33333)
  }, 3000);
})


Promise.allSettled([p2, p1, p3]).then(res => {
  console.log(res)
}).catch(err => {
  console.log("err:", err)
})
/* 
(3) [{…}, {…}, {…}]
0: {status: 'rejected', reason: 22222}
1: {status: 'fulfilled', value: 11111}
2: {status: 'fulfilled', value: 33333}
length: 3
[[Prototype]]: Array(0)
*/

12 Promise类方法race

如果有一个Promise有了结果,我们就希望决定最终新Promise的状态,那么可以使用race方法:

race是竞技、竞赛的意思,表示多个Promise相互竞争,谁先有结果,那么就使用谁的结果;

// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(11111)
  }, 1000);
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(22222)
  }, 2000);
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(33333)
  }, 3000);
})

// race: 竞技/竞赛
// 只要有一个Promise变成fulfilled状态, 那么就结束

Promise.race([p1, p2, p3]).then(res => {
  console.log("res:", res)
}).catch(err => {
  console.log("err:", err)
})
//res: 11111


// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(11111)
  }, 4000);
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(22222)
  }, 2000);
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(33333)
  }, 3000);
})


// 意外: 如果在所有的promise都还没有结果的情况下,有一个变成rejected了
//就不会等其他结果,直接调用catch回调函数
Promise.race([p1, p2, p3]).then(res => {
  console.log("res:", res)
}).catch(err => {
  console.log("err:", err)
})
//err: 22222

13 Promise类方法any

any方法是ES12中新增的方法,和race方法是类似的:

  • any方法会等到一个fulfilled状态,才会决定新Promise的状态;

  • 如果所有的Promise都是reject的,那么也会等到所有的Promise都变成rejected状态;

// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(11111)

  }, 1000);
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(22222)
  }, 500);
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(33333)

  }, 3000);
})

// any方法
//至少等到有一个结果是fulfilled
Promise.any([p1, p2, p3]).then(res => {
  console.log("res:", res)
}).catch(err => {
  console.log("err:", err.errors)
})
//res: 11111

如果所有的Promise都是reject的,那么会报一个AggregateError的错误。

// 创建多个Promise
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(11111)

  }, 1000);
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(22222)
  }, 500);
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(33333)
  }, 3000);
})

// any方法
//意外:所有都是rejected的话,会等到所有都执行完之后,最后再执行chatch的回调
Promise.any([p1, p2, p3]).then(res => {
  console.log("res:", res)
}).catch(err => {
  console.log("err:", err)
})
//err: AggregateError: All promises were rejected

//console.log("err:", err.errors)的话,会返回传入的参数
//err: (3) [11111, 22222, 33333]

相关文章

网友评论

      本文标题:Promise

      本文链接:https://www.haomeiwen.com/subject/ouneortx.html