Promise和Async知识点


Promise

前言

1、Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有执行了resolve()或reject(),才能决定当前是哪一种状态。
2、Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。假如没有调用resolve()或reject(),状态会一直是pending,但是不影响后面的js。
3、resolve和reject只能传一个参数

Promise优缺点

1、优点:可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
2、缺点:1. 无法取消Promise,一旦新建它就会立即执行,无法中途取消。2. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。3. 当处于pending状态时,无法得知目前进展到哪一个阶段

new Promise

1、new Promise 时要传入函数,函数有resolve reject 两个参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let promise2 = new Promise(function(resolve, reject){
let a = 2
setTimeout((val) => {
console.log('执行了3秒的定时器')
reject('我失败了')
}, 3000, a+1)
setTimeout(val =>{
console.log('执行了5秒的定时器')
resolve('我成功了')
}, 5000, a)
new Error()
return a
})
promise2.then((Chinese) => {console.log('success', Chinese)}, (Chinese) =>{console.log('fail', Chinese)})
// '执行了3秒的定时器'
// 'fail' '我失败了'
// '执行了5秒的定时器'
// 从上个例子说明,Promise新建后就立即执行里面的函数,成功时执行resolve(),失败时执行reject(),虽然在代码中调用了函数,但是真正执行的还要看状态。
Promise实用写法

因为Promise新建后就会立即执行里面的函数,所以一般都是定义方法

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

timeout(100).then((value) => {
console.log(value);
})catch(val => {console.log('error')}) // 不要用then(resolve, reject)方法,采用链式方法

Promise注意点

Promise新建后就立即执行,但是resolve和reject却是异步后执行的,所以等当前脚本所有同步任务完成后才会执行。只有当Promise内部语法错误的时候才会停止下面的脚本,所以只要语法没有错误,都会执行完所有脚本才执行resolve和reject。

1
2
3
4
5
6
7
8
9
10
11
12
13
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
console.log('后面的内容')
});
promise.then(function() {
console.log('resolved.');
}).catch() // 每个new Promise都要有catch,不然Promise内部有语法错误,不会终止下面js的运行
console.log('Hi!');
// Promise
// '后面的内容'
// Hi!
// resolved

// resolve和reject()只能支持传一个实参
new Promise((resolve, reject) => {
resolve(1,2)
}).then((a,b) => {console.log(a, b)}) // 1, undefined

Promise加载图片实例
1
2
3
4
5
6
7
8
9
10
11
12
function loadImageAsync(url) {
return new Promise(function(resolve, reject) {
const image = new Image();
image.onload = function() {
resolve(image);
};
image.onerror = function() {
reject(new Error('Could not load image at ' + url));
};
image.src = url;
});
}
Promise实现Ajax实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const getJSON = function(url) {
const promise = new Promise(function(resolve, reject){
const handler = function() {
if (this.readyState !== 4) {
return;
}
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
const client = new XMLHttpRequest();
client.open("GET", url);
client.onreadystatechange = handler;
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();

});

return promise;
};

getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});
Promise.prototype.then()和Promise.prototype.catch()

then/catch方法返回的是一个新的Promise实例,所以可以链式调用,有时候还需要它执行new Promise定义的方法,这时候只要返回之前的new Promise的函数就行了

1
2
3
4
5
6
7
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL); // 返回时新的Promise对象,所以还是需要执行new Promise,然后根据状态调用方法
}).then(function funcA(comments) {
console.log("resolved: ", comments);
}, function funcB(err){
console.log("rejected: ", err);
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 以下2个写法是一样的
// 写法一
const promise = new Promise(function(resolve, reject) {
try {
throw new Error('test');
} catch(e) {
reject(e);
}
});
promise.catch(function(error) {
console.log(error);
});

// 写法二
const promise = new Promise(function(resolve, reject) {
reject(new Error('test')); // reject方法的作用,等同于抛出错误。
});
promise.catch(function(error) {
console.log(error);
Promise.prototype.finally

不管Promise状态是正确还是错误,都会执行finally

1
2
3
4
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

Promise.all

Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。Promise.all接受一个promise对象的数组,待全部完成之后,统一执行success;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const p = Promise.all([p1, p2, p3]);
// p的状态由p1、p2、p3决定,分成两种情况。
// 1、只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
// 2、只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

// 生成一个Promise对象的数组
let fn = function(val){
return new Promise(function(resolve, reject){
setTimeout((val) => {
console.log(String(val).repeat(3), val);
resolve(val)
}, Number(val)*1000, val)
return val
})
}
let arr = [1,2,3,4,5].map((item) =>{return fn(item)})
Promise.all(arr).then(val => {console.log(val)})
// 111 1
// 222 2
// 333 3
// 444 4
// 555 5
// [1,2,3,4,5]

Promise.prototype.race

Promise.race接受一个包含多个promise对象的数组,只要有一个完成,就执行success

Asnc/Await

前言

async/await是写异步代码的新方式,基于Promise实现的,但是优于回调函数和Promise。函数不能阻塞后面脚本运行,只有函数里的才会等待。

用法

1、使用await,函数必须用async标识。2、await后面跟的是一个Promise实例。3、需要安装babel-polyfill,安装后记得引入 //npm i –save-dev babel-polyfill

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function loadImg(src) {
const promise = new Promise(function(resolve, reject) {
console.log('我进入了Promise')
const img = document.createElement('img')
img.onload = function() {
console.log('我完成了image加载')
resolve(img)
}
img.onerror = function() {
reject('图片加载失败')
}
img.src = src
})
return promise
}
const src1 = 'https://www.imooc.com/static/img/index/logo_new.png'
const src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'
const load = async function() {
const result1 = await loadImg(src1)
console.log('result1:')
console.log(result1) // 这里返回的不是promise
const result2 = await loadImg(src2)
console.log('result2:')
console.log(result2) // 这里返回的不是promise
}
load()
// 执行结果:我进入了Promise 我完成了image加载 result1: <img /> result2: <img />
// 当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
捕获错误
1
2
3
4
5
6
7
async function myFunction() {
try {
await somethingThatReturnsAPromise();
} catch (err) {
console.log(err);
}
}
为什么Async/Await更好

1、不需要写.then,不需要写匿名函数处理Promise的resolve值,也不需要定义多余的data变量,还避免了嵌套代码。
2、可能遇到过这样的场景,调用promise1,使用promise1返回的结果去调用promise2,然后使用两者的结果去调用promise3。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// promise
const makeRequest = () => {
return promise1()
.then(value1 => {
return promise2(value1)
.then(value2 => {
return promise3(value1, value2)
})
})
}

// 使用async/await
const makeRequest = async () => {
const value1 = await promise1()
const value2 = await promise2(value1)
return promise3(value1, value2)
}

3、条件判断更直观

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// promise
const makeRequest = () => {
return getJSON()
.then(data => {
if (data.needsAnotherRequest) {
return makeAnotherRequest(data)
.then(moreData => {
console.log(moreData)
return moreData
})
} else {
console.log(data)
return data
}
})
}

// async/await
const makeRequest = async () => {
const data = await getJSON()
if (data.needsAnotherRequest) {
const moreData = await makeAnotherRequest(data);
console.log(moreData)
return moreData
} else {
console.log(data)
return data
}
}