promise

What is Promise

Promise是JavaScript的异步操作的解决方案,为异步操作提供统一接口。

它起到代理作用(proxy), 充当异步操作与回调函数之间的中介。Promise可以让异步操作写起来,就像在写同步操作的流程,而不必一层层地嵌套回调函数。

Promise是一个对象,也是一个构造函数。

1
2
3
4
5
function f1(resolve, reject) {
//异步代码
}
var p1 = new Promise(f1);//p1是一个构造函数实例
p1.then(f2);// f1异步操作完成就会执行f2

传统方法与Promise方法对比:

1
2
3
4
5
6
7
8
step1(function(v1){
step2(v1, function(v2){
// ...
})
})

new Promise(step1)
.then(step2);

States of Promise

共三种状态:

  • pending
  • fulfilled
  • rejected

fulfille为resolved.

这三种状态的变化途径只有两种:

  • 从”未完成”到”成功”

  • 从”未完成”到”失败”

1
2
3
4
5
6
7
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
});
}

timeout(100)

then 用法解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 写法一
f1().then(function () {
return f2();
});

// 写法二
f1().then(function () {
f2();
});

// 写法三
f1().then(f2());

// 写法四
f1().then(f2);

Promise优缺点

优点:

  • 统一异步API,fetch API基于promise的:
1
2
3
fetch(url)
.then(request=>request.test())
.then(str=>...)

Promise与事件对比:

  • 更适合处理一次性的结果,不能使用Promise

Promise与回调函数相比:

  • 更干净的函数参数,回调函数的场景,主函数既有输入参数,又有输出参数。

缺点:

  • 多次触发的事件
  • 数据流
  • 不能取消执行
  • 无法获取当前执行的进度信息(比如,要在用户界面展示进度条)(Q Promise)

简单地实现Promise,并支持异步链式调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Promise(fn) {
//promise resolve时的回调函数集
this.cbs = [];
//传递给Primise处理函数的resolve
//这里直接往实例上挂个data
//然后把onReolvedCallbakc数组里的函数依次执行一边
const resolve = (value) =>{
//注意promise的then函数需要异步执行
setTimeout(()=>{
this.data = value;
this.cbs.forEach((cb)=>cb(value));
});
}

//执行用户传入的函数
//并把resolve方法交给用户执行
fn(resolve.bind(this));
}

分开来看,fn是用户传的函数,这个函数内部调用了resolve函数后,就会把promise实例上的cbs全部执行一遍,那么cbs数组中的函数从哪来呢?

1
2
3
4
5
6
7
8
9
10
Promise.prototype.then = function(onResolved) {
return new Promise((resolve)=>{
const result = onResolved(this.data);
if(result instanceof Promise) {
result.then(resolve);
} else {
resolve(result)
}
})
}

异步操作封装成Promise,实现对Ajax的封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function pajax({url=null, method='GET', dataType:'JSON', async='true'}) {
return new Promise((resolve, reject)=>{
let xhr = new XMLHttpRequest();
xhr.open(method, url, async);
xhr.responseType = dataType;
xhr.onreadystatechange = () =>{
if(xhr.readyState===4 && xhr.status===200) {
let result = xhr.responseText;
resolve(result);
}
}
xhr.onerror = (err) =>{
reject(err);
}
xhr.send();
})
}

pajax({
url: './test.json',
}).then(console.log)
.catch(console.err)

Promise then的第二个参数和catch的区别

主要区别就是,如果在then的第一个函数里抛出了异常,后面的catch能捕获到,而then的第二个函数捕获不到。

Promise的catch是个语法糖,还是通过then来处理的,类似于:

1
2
3
Promise.prototype.catch = function(fn) {
return this.then(null, fn);
}

捕获错误信息的时候会就近原则。