半理解系列--Promise的进化史
学过js的都知道,程序有同步编程和异步编程之分,同步就好比流水线,一步一个脚印,做完上个任务才做下一个,异步编程好比客服,客服接了一个电话,收到了一个任务,然后把任务交给另外的人来处理,同时,继续接听下一个电话,等到另外的人处理完任务了,再通知客服,客服再反馈给第一个打电话的人。异步编程一般用来调取接口拉数据。
通过我描述的篇幅,就知道异步编程比同步编程麻烦许多。远古时期,异步编程是通过回调函数来解决的。但是回调函数会有回调地狱的问题,回调的多了,维护人员看起来头都大了,好比:taskC需要等待taskB做完(taskC才执行),taskB又需要等待taskA做完(taskB才执行)
function taskA (cb) {
//..do task A
cb()
}
function taskB(cb){
//..do task B
cb()
}
function taskC(cb){
//..do task C
cb()
}
taskA(function(){
taskB(function(){
taskC()
})
})
...以此类推,不断循环嵌套,最终陷入地狱
而Promise就把这一系列的回调,通过链式调用的方式连接起来,看起来清爽多了。同样是上面的代码,Promise可以这么写(伪代码)
new Promise().then(taskA).then(taskB).then(taskC)
promise的使用
const promise = new Promise((resolve,reject)=>{
if (/*do something done*/){
resolve() // 可在此传参数
} else {
// do something fail
reject() // 可在此传参数
}
})
promise.then(()=>{
//do something
}).catch(e => { throw e})
上面的resolve
,可以当作task函数的cb回调函数,当resolve()
执行的时候,then
方法中的回调会被执行,如果是reject
执行,错误会被catch
捕捉。
Promise的静态方法
上面说的then
和catch
都是Promise的原型方法,即Promise.prototype.then/catch
Promise本身有两个静态方法,其作用类似 new Promise()
Promise.resolve()
const promise1 = Promise.resolve()
等价于
const promise2 = new Promise((reslove)=>{
reslove()
})
使用该方法调用then
方法
Promise.reject()
const promise1 = Promise.reject()
等价于
const promise2 = new Promise((resolve,reject)=>{
reject()
})
使用该方法会被catch
捕捉
Promise的链式调用
Promise的实例对象的then方法是可以重复调用的,then方法返回的是一个promise实例对象,所以可以重复调用then方法,并且(敲黑板),上一个then方法的返回值,会作为下一个then方法的参数传递
举个栗子:
const promise = Promise.resolve('start')
promise.then((params)=>{
console.log(params) // start
return 'aa'
}).then((params) => {
console.log(params) // aa
return 'bb'
}).then((params)=>{
console.log(params) // bb
return 'cc'
})
// 最后会返回一个状态是resolve(cc)的promise对象:Promise {: "cc"}
深入一下(又不会怀孕)
function badAsyncCall() {
var promise = Promise.resolve();
promise.then(function() {
// 任意处理
return 'newVar';
});
return promise;
}
// 修改一下
function goodAsyncCall() {
var promise = Promise.resolve();
return promise.then(function() {
// 任意处理
return 'newWar';
});
}
以上两个写法是不是很相似,唯一不同的就是return的处理。但调用,badAsynccall
会出错,而anAsyncCall
能正确执行,比如:
badAsyncCall().then(params => { console.log('bad--',params)}) // bad-- undefined
goodAsyncCall().then(params => { console.log('good--',params)}) // good-- newWar
分析:第一种,错误写法,首先在 promise.then 中产生的异常不会被外部捕获,此外,也不能得到 then 的返回值,即使其有返回值。
原因:由于每次 promise.then 调用都会返回一个新创建的promise对象,第一种返回的promise,相当于没有调用过函数内部的then方法,是一个全新的promise实例对象
结论: 统一使用promise链式调用,如:promise.then(taskA).then(taskB)
### async&await和promise的前世缘缘
promise说白了还是用回调的方式来解决异步问题,跟真正同步还是有差距的。
异步编程的最高境界,就是根本不用关心它是不是异步!(来之ruanyifeng老师的话)
所以,async&await方案出现了
用法:
function readFile(fileName) {
return new Promise((resolve,reject)=>{
fs.readFile(fileName, function(error, data) {
if (error) return reject(error);
resolve(data); // 向thenc传送异步读取文件的数据
});
})
}
// 调用
readFile(fileName).then(function(data){
console.log('prmoise read files data --', data)
})
// 等价于
async function asyncFn(fileName){
const data = await readFile(fileName)
console.log('await data --', data)
return data
}
asyncFn(fileName)
写法是不是简洁了许多!
其实async就是一个Promise的语法糖,它的返回值是一个promise对象,因此可以用then方法做链式调用(但参数就是async函数中的返回值,如上文的data!!)
async函数中还可以不使用promise,比如:
async function asyncFn(){
const data = await setTimeout(function(){
console.log('setTimeout')
return 'data'
},1000)
console.log('data',data) // Timeout {} 对象
}
console.log('async',asyncFn()) // Promise { }
但这两者,其实经常混用,常见的就是readFile函数的做法啦
看懂以上的,才大家出一道题看看能不能懂;
async function asynFn(){
await Promise.resolve('aaa')
const data = {
b:'bb',
c:function(){ return this.b }
}
return data //return 作为参数传递给then then的chain链也是通过return参数来不断传递给后面的then
}
var cball = asynFn()
cball.then(function(data){
console.log('data:',data)
})
还有一种异步编程的语法糖: * & yield
跟async基本一样,不在本文讨论的重点。有兴趣自行google啦
参考资料:
网友评论