还是想谈谈Promise
一、你真的了解异步回调吗
众所周知,JavaScript是一门单线程的语言,可不要把它的单线程与浏览器的执行搞混了。浏览器依旧是多线程执行的,发请求的进程发请求,渲染UI的进程渲染UI,不一样的是,JS的执行引擎进程和UI的渲染进程是互斥的(这也就是为什么我们要求最好将 < script>标签放在页面的body元素后面,避免因为等待JS加载执行页面得不到渲染出现白屏的现象)如果你不了解为什么JS是一门单线程的语言,emmm当我没说
单线程存在的缺点也是显而易见的,一次只能做一件事情这样未免效率也太低了点吧。此时就不得不提JavaScript中闪闪发光的回调函数了,解放生产力的好助手。
于是乎,你在不经意间就写出了这样的代码。。。
getDirectories(function(dirs) {
getFiles(dirs[0], function(files) {
getContent(files[0], function(file, content) {
console.log('filename:', file);
console.log(content);
});
});
});
竟然有点好看??其实界面上的排版布局在我看来都是小事,毕竟这代码隔个十天半个月不看再回头想必作者自己也是抓耳挠腮看不懂的,那么问题在哪里呢?
试问,这样的代码,每一个函数都附带了另外的一个函数调用,错误如何捕捉?每一步的堆栈调用顺序是什么?
开始觉得好像这样的编程风格确实是有点问题的,那不如让我们一起看看 promise吧!
二、关于Promise
- 回调任务顺序
前面我们有提到过,回调就是可以让cpu去执行当前执行栈内的任务,将那些需要条件触发的回调任务放入到任务队列中,在执行完当前栈内的任务后便去询问任务队列中是否存在未执行的任务再取出执行。问题的关键是:如何判定这些回调任务执行完毕的顺序呢?
Promise也帮我们考虑到了这一点,每一个函数只会在前一个 promise 被调用并且完成回调后调用,并且这个函数会被前一个 promise 的输出调用。也就是说,不论你的回调任务是否异步,都必须通过.then来进行触发,大大解决了顺序问题呀!它的精彩之处还远不止这些 - promise的for循环 — promise.all
let wake = (time) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${time / 1000}秒后醒来`)
}, time)
})
}
let p1 = wake(3000)
let p2 = wake(2000)
Promise.all([p1, p2]).then((result) => {
console.log(result) // [ '3秒后醒来', '2秒后醒来' ]
}).catch((error) => {
console.log(error)
})
Promise.all()会以一个 promises 数组为输入,并且返回一个新的 promise。这个新的 promise 会在数组中所有的 promises 都成功返回后才返回。他是异步版的 for 循环。并且这里返回的promise是以我们传入数组的顺序为准的,即不必担心这些异步promise完成的先后顺序。并且一旦数组中的 promise 任意一个返回错误,Promise.all() 也会返回错误。
- promise–race
Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。 - promise–catch/then
每一个promise实例身上都带有一个then方法(catch其实是then(null, function。。。)的一个语法糖)
let p = new Promise(function(){
throw new Error('wrong1')
}).then(function(){
throw new Error('wrong2')
}).then(function(){
throw new Error('wrong3')
})
.catch(function(error){
console.log(error) //wrong1
})
我们在每一个then方法的内部可以做三件事:
1. return 另一个 promise
2. return 一个同步的值 (或者 undefined)
3. throw 一个同步异常
- promises 穿透
Promise.resolve('foo').then(Promise.resolve('bar')).then(function (result) {
console.log(result); //foo
});
是不是对这个结果感到大吃一惊!其实原因很简单
发生这个的原因是如果你向 then() 传递的并非是一个函数(比如 promise),它实际上会将其解释为 then(null),这就会导致前一个 promise 的结果会穿透下面。简单的说,你可以直接传递一个 promise 到 then() 函数中,但是它并不会按照你期望的去执行。then() 是期望获取一个函数
因此记住:永远都是往 then() 中传递函数!
这篇文章应该算的上是promise的进阶版理解,其实不应该一上来就总结这么复杂的
后续会更两篇关于promise从0到1实现的文章
参考资料
We have a problem with promises
理解和使用Promise.all和Promise.race