号外号外:本篇文章主要使用了ES6箭头函数,如果不熟悉的同学建议看一看。嘻嘻

开篇之前先理清下面几个问题
Promise的构造函数参数可以传递什么类型?除了使用Promise构造函数进行链式调用外,Promise还有什么方式进行链式调用吗?
Promise.resolve()的参数可以传递什么类型的数据?不同类型的数据产生怎样的结果?
Promise的构造函数中调用resolve()/reject()这样做的目的是什么?
Promise的then方法是做什么的?
如何终止链式调用?(这个场景会在什么时候遇到)
如何把promise的代码转化为async await的调用方式
进阶:手写Promise(哈哈 是不是有点招人恨的样子…)

从第一个问题开始:Promise的构造函数参数必须是函数,函数有两个参数一个是resolve,一个是reject。除了Promise构造函数进行链式调用外,Promise.resolve()也可以进行链式调用,不过他的参数多数是基本数据类型会进行值穿透。
第二个问题:Promise.resolve()的参数是基本数据类型,会导致值传递
第三个问题:Promise的构造函数中调用resolve()/reject()这样做的目的是触发then的回调函数(resolve调用成功的回调,reject调用失败的回调)并且修改Promise的状态。状态有pending 转化为fulfilled或者rejected。
第四个问题:Promise的then方法可以返回上一个Promise的回到结果,也可以return一个新的Promise,从而实现链式调用
第五个问题:then返回一个空的Promise对象即可
第六个问题:
第七个问题:

为什么使用promise

解决回调地狱的代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象 。其次promise可以支持多个并发的请求,获取并发请求中的数据

常规回调方式实现:

  1. setTimeout(() => {
  2. console.log('1');
  3. setTimeout(() => {
  4. console.log('2');
  5. setTimeout(() => {
  6. console.log('3');
  7. setTimeout(() => {
  8. console.log('4');
  9. }, 1000);
  10. }, 1000);
  11. }, 1000);
  12. }, 1000);

promise方式实现:

  1. function getStr1() {
  2. return new Promise((resolve, reject) => {
  3. setTimeout(function () {
  4. resolve('1');
  5. }, 1000);
  6. });
  7. }
  8. function getStr2() {
  9. return new Promise((resolve, reject) => {
  10. setTimeout(function () {
  11. resolve('2');
  12. }, 1000);
  13. });
  14. }
  15. function getStr3() {
  16. return new Promise((resolve, reject) => {
  17. setTimeout(function () {
  18. resolve('3');
  19. }, 1000);
  20. });
  21. }
  22. function getStr4() {
  23. return new Promise((resolve, reject) => {
  24. setTimeout(function () {
  25. resolve('4');
  26. }, 1000);
  27. });
  28. }
  29. getStr1().then((data) => {
  30. console.log(data);
  31. return getStr2();
  32. }).then((data) => {
  33. console.log(data);
  34. return getStr3();
  35. }).then((data) => {
  36. console.log(data);
  37. return getStr4();
  38. }).then((data) => {
  39. console.log(data);
  40. })

如果我们使用Promise去实现这个效果,虽然代码量不会减少,甚至更多,但是却大大增强了其可读性和可维护性

解密Promise核心

通过上面几个问题能感受到Promise更像一个保存状态的魔法罐,只要状态改变就可以就能一直传递下去,并且状态持久的保存在自己身上,随时都可以then函数的调用。其次状态是不可逆的,只能从pending 转化为fulfilled或者rejected。总结一句话就是状态就是Promise的灵魂所在,状态使用resolve或者reject管理的。接下来通过几个例子来理解Promise
例一:
首先需要知道Promise 构造函数是同步执行的,promise.then 中的函数是异步执行的,方便下面工作进行

  1. let promise = new Promise((resolve, reject) => {
  2. setTimeout(() => {
  3. resolve('success')
  4. }, 3000)
  5. })
  6. promise.then((data) => {
  7. console.log(data)
  8. console.log('resolved===', promise)
  9. })
  10. console.log('pengding====', promise)

输出结果:
image.png
如果我这里把resolve(‘success’)注释掉会发生什么事情呢?有的同学会说then函数不再往下执行,恭喜你答对了,给你一个大大的赞!!!
image.png
就像上面七个问题中提到过的一样,resolve触发Promise状态的改变,然后触发then回调函数的调用,如果注释resolve(‘success’),那么整个链路就暂停了。是不是瞬间感受到状态是如何支撑Promise 运转的了。接下来再看看Promise 具有状态缓存的特性,国际惯例还是用例子引出我们的理论

  1. let promise = new Promise((resolve, reject) => {
  2. setTimeout(() => {
  3. resolve('success')
  4. }, 3000)
  5. })
  6. promise.then((data) => {
  7. console.log("data1===" + data)
  8. console.log('resolved===', promise)
  9. })
  10. promise.then((data) => {
  11. console.log("data2===" + data)
  12. console.log('resolved===', promise)
  13. })
  14. promise.then((data) => {
  15. console.log("data3===" + data)
  16. console.log('resolved===', promise)
  17. })
  18. console.log('pengding====', promise)

image.png
只要状态从pending 转化为resolved或者rejected之后,不管我们什么时候调用,都能拿到回调结果,但是这样做有什么好处呢?

回调函数

我们花费了大半篇文稿讲述Promise的状态管理,目的是什么?总不能做事情没目的吧!作为名侦探柯南的黑粉我要出来指证!!!目的就是触发then函数拿到回调结果,然后就可以为所欲为了。接下来就看看怎么拿到回调函数的

  1. let promise = new Promise((resolve, reject) => {
  2. setTimeout(() => {
  3. resolve('success')
  4. // reject("fail")
  5. }, 1000)
  6. }).then(resolved => {
  7. console.log("resolved===" + resolved)
  8. }, rejected => {
  9. console.log("rejected===" + rejected)
  10. })
  11. console.log('pengding====', promise)

then函数传入两个参数,第一个参数代表成功的回调,第二个参数代表失败的回调,注意参数使用函数的形式,如果参数使用基本数据类型就会出现值穿透,小伙伴后果自负哟!!!函数的参数就是我们的回调结果,是不是还是以往的那么稳妥。对于没有成功回调的我们可以使用null代替,例子

  1. let promise = new Promise((resolve, reject) => {
  2. setTimeout(() => {
  3. // resolve('success')
  4. reject("fail")
  5. }, 1000)
  6. }).then(null, rejected => {
  7. console.log("rejected===" + rejected)
  8. return new Promise((resolve, reject) => {
  9. reject(100)
  10. })
  11. }).then(null,reject => {
  12. console.log("=====" + reject)
  13. })

对于then的参数期望是函数,传入非函数则会发生值穿透。

  1. Promise.resolve(1)
  2. .then(2)
  3. .then("3")
  4. .then(data => { console.log(data) })

image.png

链式调用

既然我们已经知道如何通过改变Promise的状态然后拿到回调结果,那我们就很容易实现链式调用了。这里就不卖关子了,我们让then回调函数返回一个新的Promise,这样就实现了链式调用,是不是很机智!!!至此那个男孩从此不再那么天真。。。一切没有使用案例的理论都是耍流氓,这里问题不大,不就是一个案例吗???哭笑脸

  1. let peopleObj = { name: "taowuhua", age: 18 }
  2. let promise = new Promise((resolve, reject) => {
  3. setTimeout(() => {
  4. resolve('success')
  5. }, 100)
  6. }).then(resolved => {
  7. console.log("第一次链式调用结果" + resolved)
  8. return new Promise((resolve, reject) => {
  9. resolve(JSON.stringify(peopleObj))
  10. })
  11. }).then(resolved => {
  12. console.log("第二次链式调用结果" + resolved)
  13. return new Promise((resolve, reject) => {
  14. reject("2")
  15. })
  16. }, rejected => {
  17. })

image.png
一切都是那么的美好,任何人都不是 一帆风顺总会遇到挫折程序也不例外,中途遇到异常怎么办?怎么办?怎么办?我好害怕!!!这个时候catch从天而降,一切又回到原来的状态,不再害怕不再担心

  1. let promise = new Promise((resolve, reject) => {
  2. console.log(taowuhua)
  3. }).then(resolved => {
  4. return new Promise((resolve, reject) => {
  5. resolve(1)
  6. })
  7. }).catch(rejected => {
  8. console.log(rejected)
  9. })

image.png
到此链式调用就算说拜拜的时候到了。

then中 return 一个 error 对象并不会抛出错误,所以不会被后续的 catch捕获

  1. let promise = new Promise((resolve, reject) => {
  2. resolve("taowuhua")
  3. }).then(resolved => {
  4. return new Error("总是抓不住那颗受伤的心...")
  5. }).catch(rejected => {
  6. console.log(rejected)
  7. })
  1. let promise = new Promise((resolve, reject) => {
  2. resolve("taowuhua")
  3. }).then(resolved => {
  4. return new Error("总是抓不住那颗受伤的心...")
  5. }).then((resolve) => {
  6. console.log(resolve)
  7. }, (reject) => {
  8. console.log(reject)
  9. }).catch(rejected => {
  10. console.log(rejected)
  11. })

Promise常用Api

Promise.resolve

方法返回一个状态为resolved的promise实例

  1. Promise.resolve('foo');//等价于如下
  2. new Promise((resolve,reject)=>{
  3. resolve('foo');
  4. })

Promise.reject

方法返回一个状态为rejected的promise实例

  1. Promise.reject('foo');//等价于如下
  2. new Promise((resolve,reject)=>{
  3. reject('foo');
  4. })

Promise.all

  1. function getStr1() {
  2. return new Promise((resolve, reject) => {
  3. setTimeout(function () {
  4. resolve('1');
  5. }, 1000);
  6. });
  7. }
  8. function getStr2() {
  9. return new Promise((resolve, reject) => {
  10. setTimeout(function () {
  11. resolve('2');
  12. }, 1000);
  13. });
  14. }
  15. function getStr3() {
  16. return new Promise((resolve, reject) => {
  17. setTimeout(function () {
  18. resolve('3');
  19. }, 1000);
  20. });
  21. }
  22. function getStr4() {
  23. return new Promise((resolve, reject) => {
  24. setTimeout(function () {
  25. resolve('4');
  26. }, 1000);
  27. });
  28. }
  29. Promise.all([getStr1(), getStr2(), getStr3(), getStr4()]).then((values) => {
  30. console.log(values);
  31. });

异步代码同步化

异步代码同步化并不是异步代码就变成了同步代码了,而是视觉上像同步代码在运行是的。

async await

建议移步这个链接:https://segmentfault.com/a/1190000007535316
async 用于申明一个 function 是异步的,async 函数返回 Promise为resolved状态的构造函数。而 await 用于等待一个异步方法执行完成

async函数有返回值(类似 Promise.resolve(x) )

  1. async function testAsync() {
  2. return "hello async";
  3. }
  4. const result = testAsync();
  5. console.log(result);

image.png

  1. async function testAsync() {
  2. return "hello async";
  3. }
  4. testAsync().then(v => {
  5. console.log(v); // 输出 hello async
  6. });

async函数没有返回值(类似 Promise.resolve(undefined) )

  1. async function testAsync() {
  2. }
  3. const result = testAsync();
  4. console.log(result);

image.png

await等什么

如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果

假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。我们仍然用 setTimeout 来模拟异步操作:

  1. /**
  2. * 传入参数 n,表示这个函数执行的时间(毫秒)
  3. * 执行的结果是 n + 200,这个值将用于下一步骤
  4. */
  5. function takeLongTime(n) {
  6. return new Promise(resolve => {
  7. setTimeout(() => resolve(n + 200), n);
  8. });
  9. }
  10. function step1(n) {
  11. console.log(`step1 with ${n}`);
  12. return takeLongTime(n);
  13. }
  14. function step2(n) {
  15. console.log(`step2 with ${n}`);
  16. return takeLongTime(n);
  17. }
  18. function step3(n) {
  19. console.log(`step3 with ${n}`);
  20. return takeLongTime(n);
  21. }

现在用 Promise 方式来实现这三个步骤的处理

  1. function doIt() {
  2. console.time("doIt");
  3. const time1 = 300;
  4. step1(time1)
  5. .then(time2 => step2(time2))
  6. .then(time3 => step3(time3))
  7. .then(result => {
  8. console.log(`result is ${result}`);
  9. console.timeEnd("doIt");
  10. });
  11. }
  12. doIt();

经历了上面的磨难可以出去社会闯荡一下了,先从下面的两个小demo试试

  1. Promise.resolve(1)
  2. .then((res) => {
  3. return Promise.resolve(4)
  4. })
  5. .then(5)
  6. .then((res) => 2)
  7. .then(Promise.resolve(3))
  8. .then(data => {
  9. console.log(data)
  10. })
  1. Promise.resolve(Promise.resolve(3))
  2. .then((res) => {
  3. return Promise.resolve(4)
  4. })
  5. .then(5)
  6. .then((res) => 2)
  7. .then(Promise.resolve(3))
  8. .then(data => {
  9. console.log(data)
  10. })

这里还有一部分面试题感兴趣的同学可以研究下。直通车:https://juejin.im/post/5a04066351882517c416715d