号外号外:本篇文章主要使用了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可以支持多个并发的请求,获取并发请求中的数据
常规回调方式实现:
setTimeout(() => {console.log('1');setTimeout(() => {console.log('2');setTimeout(() => {console.log('3');setTimeout(() => {console.log('4');}, 1000);}, 1000);}, 1000);}, 1000);
promise方式实现:
function getStr1() {return new Promise((resolve, reject) => {setTimeout(function () {resolve('1');}, 1000);});}function getStr2() {return new Promise((resolve, reject) => {setTimeout(function () {resolve('2');}, 1000);});}function getStr3() {return new Promise((resolve, reject) => {setTimeout(function () {resolve('3');}, 1000);});}function getStr4() {return new Promise((resolve, reject) => {setTimeout(function () {resolve('4');}, 1000);});}getStr1().then((data) => {console.log(data);return getStr2();}).then((data) => {console.log(data);return getStr3();}).then((data) => {console.log(data);return getStr4();}).then((data) => {console.log(data);})
如果我们使用Promise去实现这个效果,虽然代码量不会减少,甚至更多,但是却大大增强了其可读性和可维护性
解密Promise核心
通过上面几个问题能感受到Promise更像一个保存状态的魔法罐,只要状态改变就可以就能一直传递下去,并且状态持久的保存在自己身上,随时都可以then函数的调用。其次状态是不可逆的,只能从pending 转化为fulfilled或者rejected。总结一句话就是状态就是Promise的灵魂所在,状态使用resolve或者reject管理的。接下来通过几个例子来理解Promise
例一:
首先需要知道Promise 构造函数是同步执行的,promise.then 中的函数是异步执行的,方便下面工作进行
let promise = new Promise((resolve, reject) => {setTimeout(() => {resolve('success')}, 3000)})promise.then((data) => {console.log(data)console.log('resolved===', promise)})console.log('pengding====', promise)
输出结果:
如果我这里把resolve(‘success’)注释掉会发生什么事情呢?有的同学会说then函数不再往下执行,恭喜你答对了,给你一个大大的赞!!!
就像上面七个问题中提到过的一样,resolve触发Promise状态的改变,然后触发then回调函数的调用,如果注释resolve(‘success’),那么整个链路就暂停了。是不是瞬间感受到状态是如何支撑Promise 运转的了。接下来再看看Promise 具有状态缓存的特性,国际惯例还是用例子引出我们的理论
let promise = new Promise((resolve, reject) => {setTimeout(() => {resolve('success')}, 3000)})promise.then((data) => {console.log("data1===" + data)console.log('resolved===', promise)})promise.then((data) => {console.log("data2===" + data)console.log('resolved===', promise)})promise.then((data) => {console.log("data3===" + data)console.log('resolved===', promise)})console.log('pengding====', promise)

只要状态从pending 转化为resolved或者rejected之后,不管我们什么时候调用,都能拿到回调结果,但是这样做有什么好处呢?
回调函数
我们花费了大半篇文稿讲述Promise的状态管理,目的是什么?总不能做事情没目的吧!作为名侦探柯南的黑粉我要出来指证!!!目的就是触发then函数拿到回调结果,然后就可以为所欲为了。接下来就看看怎么拿到回调函数的
let promise = new Promise((resolve, reject) => {setTimeout(() => {resolve('success')// reject("fail")}, 1000)}).then(resolved => {console.log("resolved===" + resolved)}, rejected => {console.log("rejected===" + rejected)})console.log('pengding====', promise)
then函数传入两个参数,第一个参数代表成功的回调,第二个参数代表失败的回调,注意参数使用函数的形式,如果参数使用基本数据类型就会出现值穿透,小伙伴后果自负哟!!!函数的参数就是我们的回调结果,是不是还是以往的那么稳妥。对于没有成功回调的我们可以使用null代替,例子
let promise = new Promise((resolve, reject) => {setTimeout(() => {// resolve('success')reject("fail")}, 1000)}).then(null, rejected => {console.log("rejected===" + rejected)return new Promise((resolve, reject) => {reject(100)})}).then(null,reject => {console.log("=====" + reject)})
对于then的参数期望是函数,传入非函数则会发生值穿透。
Promise.resolve(1).then(2).then("3").then(data => { console.log(data) })
链式调用
既然我们已经知道如何通过改变Promise的状态然后拿到回调结果,那我们就很容易实现链式调用了。这里就不卖关子了,我们让then回调函数返回一个新的Promise,这样就实现了链式调用,是不是很机智!!!至此那个男孩从此不再那么天真。。。一切没有使用案例的理论都是耍流氓,这里问题不大,不就是一个案例吗???哭笑脸
let peopleObj = { name: "taowuhua", age: 18 }let promise = new Promise((resolve, reject) => {setTimeout(() => {resolve('success')}, 100)}).then(resolved => {console.log("第一次链式调用结果" + resolved)return new Promise((resolve, reject) => {resolve(JSON.stringify(peopleObj))})}).then(resolved => {console.log("第二次链式调用结果" + resolved)return new Promise((resolve, reject) => {reject("2")})}, rejected => {})

一切都是那么的美好,任何人都不是 一帆风顺总会遇到挫折程序也不例外,中途遇到异常怎么办?怎么办?怎么办?我好害怕!!!这个时候catch从天而降,一切又回到原来的状态,不再害怕不再担心
let promise = new Promise((resolve, reject) => {console.log(taowuhua)}).then(resolved => {return new Promise((resolve, reject) => {resolve(1)})}).catch(rejected => {console.log(rejected)})

到此链式调用就算说拜拜的时候到了。
then中 return 一个 error 对象并不会抛出错误,所以不会被后续的 catch捕获
let promise = new Promise((resolve, reject) => {resolve("taowuhua")}).then(resolved => {return new Error("总是抓不住那颗受伤的心...")}).catch(rejected => {console.log(rejected)})
let promise = new Promise((resolve, reject) => {resolve("taowuhua")}).then(resolved => {return new Error("总是抓不住那颗受伤的心...")}).then((resolve) => {console.log(resolve)}, (reject) => {console.log(reject)}).catch(rejected => {console.log(rejected)})
Promise常用Api
Promise.resolve
方法返回一个状态为resolved的promise实例
Promise.resolve('foo');//等价于如下new Promise((resolve,reject)=>{resolve('foo');})
Promise.reject
方法返回一个状态为rejected的promise实例
Promise.reject('foo');//等价于如下new Promise((resolve,reject)=>{reject('foo');})
Promise.all
function getStr1() {return new Promise((resolve, reject) => {setTimeout(function () {resolve('1');}, 1000);});}function getStr2() {return new Promise((resolve, reject) => {setTimeout(function () {resolve('2');}, 1000);});}function getStr3() {return new Promise((resolve, reject) => {setTimeout(function () {resolve('3');}, 1000);});}function getStr4() {return new Promise((resolve, reject) => {setTimeout(function () {resolve('4');}, 1000);});}Promise.all([getStr1(), getStr2(), getStr3(), getStr4()]).then((values) => {console.log(values);});
异步代码同步化
异步代码同步化并不是异步代码就变成了同步代码了,而是视觉上像同步代码在运行是的。
async await
建议移步这个链接:https://segmentfault.com/a/1190000007535316
async 用于申明一个 function 是异步的,async 函数返回 Promise为resolved状态的构造函数。而 await 用于等待一个异步方法执行完成
async函数有返回值(类似 Promise.resolve(x) )
async function testAsync() {return "hello async";}const result = testAsync();console.log(result);

async function testAsync() {return "hello async";}testAsync().then(v => {console.log(v); // 输出 hello async});
async函数没有返回值(类似 Promise.resolve(undefined) )
async function testAsync() {}const result = testAsync();console.log(result);
await等什么
如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果
假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。我们仍然用 setTimeout 来模拟异步操作:
/*** 传入参数 n,表示这个函数执行的时间(毫秒)* 执行的结果是 n + 200,这个值将用于下一步骤*/function takeLongTime(n) {return new Promise(resolve => {setTimeout(() => resolve(n + 200), n);});}function step1(n) {console.log(`step1 with ${n}`);return takeLongTime(n);}function step2(n) {console.log(`step2 with ${n}`);return takeLongTime(n);}function step3(n) {console.log(`step3 with ${n}`);return takeLongTime(n);}
现在用 Promise 方式来实现这三个步骤的处理
function doIt() {console.time("doIt");const time1 = 300;step1(time1).then(time2 => step2(time2)).then(time3 => step3(time3)).then(result => {console.log(`result is ${result}`);console.timeEnd("doIt");});}doIt();
经历了上面的磨难可以出去社会闯荡一下了,先从下面的两个小demo试试
Promise.resolve(1).then((res) => {return Promise.resolve(4)}).then(5).then((res) => 2).then(Promise.resolve(3)).then(data => {console.log(data)})
Promise.resolve(Promise.resolve(3)).then((res) => {return Promise.resolve(4)}).then(5).then((res) => 2).then(Promise.resolve(3)).then(data => {console.log(data)})
这里还有一部分面试题感兴趣的同学可以研究下。直通车:https://juejin.im/post/5a04066351882517c416715d
