0%

你不知道的JavaScript之Promise(异步流控制)

目标:

  1. 理解 Promise(什么是 Promise,有什么好处,特点,怎么用)

什么是异步?

异步是相对于同步来说的.同步的意思就是在发起一个操作的时候,程序需要等待,直到操作完成才会进行下一步.
而异步就是,一开始一个操作的时候,程序在操作完成前可以继续后续的工作,当操作完成后,触发事件或者回调来处理操作完成的手续.
最经典的异步例子就是ajax和定时器.
例如如果所有数据都要同步请求的话,那么有一些数据发生了变化,我们就需要刷新页面才能获取.
但是异步的话,例如地图,或者搜索框搜索,这些,我们只要再次请求一次然后渲染到页面就可以了.(当然,这是对于ajax来说的异步)

Promise

回调就是指传入一些东西的时候,或者执行了什么的时候,就执行一个 function,也就是常见的 f(xxx,function(){})

定义/概念

  1. 帮助解决只用回调实现异步的严重缺陷.

  2. Promise 不是对回调的替代.
    Promise 在回调代码和将要执行这个任务的异步代码之间提供了一种可靠的中间机制来管理回调.

  3. 可以把 Promise 看作事件监听者.
    可以在其上注册以监听某个事件,在任务完成后得到通知.(只触发一次)

  4. 可以把 Promise 链接在一起
    就是把一系列异步完成的步骤串联起来

  5. 可以把 Promise 看作一个未来值
    也就是把一个值放到 Promise 里面,等到 Promise 决议时立即把这个值提取出来.
    换句话说: Promise 可以看作是同步函数返回值的异步版本.

  6. Promise 的决议结果只有两种可能: 完成或拒绝,附带一个可选的单个值.
    完成得到完成值,拒绝得到拒绝值(拒绝的原因).

  7. Promise 只决议一次,之后再试图完成或拒绝的动作都会被忽略.

  8. Promise 对只用回调的异步方法给予了重大的进步,即提供了有序性,可测性和可靠性.

  9. Promise 对象: 代表了未来某个将要发生的事件(通常是一个异步操作)

  10. 可以将异步操作以同步的流程表达出来,避免了层层嵌套的回调函数(俗称:回调地狱)

  11. ES6 的 Promise 是一个构造函数,用来生成 promise 实例

构造和使用 Promise

  1. 构造一个 Promise 实例(对象)
1
2
3
4
5
6
var p = new Promise(function(resolve, reject) {
// 两个参数都是函数,reslove()和reject()
// 如果调用reject(),这个promise被拒绝
// 如果调用resolve(),并且没有传入参数或者传入了任何非promise值,这个promise完成.
// 如果调用resolve(),并且传入了一个promise,这个promise的结果就会是传入的promise的状态(要么实现,要么拒绝)--不管是立即还是最终.
});
  1. 重构 ajax 回调例子
1
2
3
4
5
6
7
8
9
10
11
function ajax(url, cb) {
// 建立请求,最终会回调cb()
}

ajax("http://xxx.xx/xx", function handler(err, contents) {
if (err) {
// 处理ajax错误
} else {
// 处理contents成功情况
}
});

使用 Promise 重构后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function ajax(url) {
return new Promise(function pr(resolve, reject) {
// 建立请求,最终回调resolve()或reject()
});
}

ajax("http://xxx.xx/xx").then(
function fulfilled(contents) {
// 处理contents成功情况
},
function rejected(reason) {
// 处理ajax出错原因
}
);

使用场景

  1. 异步操作
    上传操作实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
beforeUpload() {
const _self = this
return new Promise((resolve, reject) => { // 从这里return一个Promise(做一件事情)
getToken().then(response => {
const key = response.data.qiniu_key
const token = response.data.qiniu_token
_self._data.dataObj.token = token
_self._data.dataObj.key = key
resolve(true) // 完成了,执行resolve(),返回true. (当到时候执行beforeUpload()的时候就能到这里的话,就是beforeUpload().then(res=>console.log(res) //输出true))
}).catch(err => {
console.log(err)
reject(false) // 拒绝了,执行reject(),返回false. (当到时候执行beforeUpload()的时候不能完成上面resolve()前的操作的话,就是beforeUpload().catch(err=>console.log(err) //输出false))
})
})
}

以上代码实现一个场景: 点击一个上传按钮->先执行beforeUpload()来做一些上传之前的准备->获取key和token完成了(resolve 了),那么就可以进入.then(res=>console.log(‘key和token获取成功,可以执行上传操作了’)))了. x>如果获取失败了,进入.catch(err=>alert(‘不要意思,获取key失败,不能执行上传操作’))


参考书籍: 《你不知道的 JavaScript》下卷