0%

异步函数策略

实现sleep

1
2
3
4
5
6
7
8
9
async function sleep(delay){
return new Promise((resolve)=>setTimeout(resolve,delay));
}
async function foo(){
const t0=Date.now();
await sleep(1500);//暂停1500ms,await会期望等到一个实现thenable接口的对象,没有的话也会将其包装为promise
console.log(Date.now()-t0);

}

实现平行加速

promise没有按顺序执行,但是await按顺序接收到每个promise的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
async function randomDelay(id){
const delay=Math.random()*1000;
return new Promise((resolve)=>setTimeout(()=>{
console.log(`${id} finished`);
resolve()
},delay))
}
async function foo(){
const t0=Date.now()
const p0=randomDelay(0);
const p1=randomDelay(1);
const p2=randomDelay(2);
const p3=randomDelay(3);
await p0;
await p1;
await p2;
await p3;
setTimeout(console.log,0,`${Date.now()-t0} ms elapsed`)
}
foo();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
async function randomDelay(id){
const delay=Math.random()*1000;
return new Promise((resolve)=>setTimeout(()=>{
console.log(`${id} finished`);
resolve(id)
},delay))
}
async function foo(){
const t0=Date.now()
const promises=Array(5).fill(null).map((key,index)=>randomDelay(index));
for(const p of promises){
console.log(`awaited ${await p}`)
}
setTimeout(console.log,0,`${Date.now()-t0} ms elapsed`)
}
foo();

Promise.all原理

只有传入的所有Promise都完成,返回promise才能完成,如果有任何promise被拒绝,返回的主promise就立即拒绝(抛弃任何其他promise的结果),如果全部完成,你会得到一个数组,其中包含传入的所有promise的完成值,对于拒绝的情况,你只会的到第一个拒绝promise的拒绝理由值,这种模式成为门,所有人到齐了才开门,传入空数组会立即完成

1
2
3
4
5
6
7
8
9
10
11
12
13
Promise.all=function(promises){
const res=[];
return new Promise((resolve,reject)=>{
promises.forEach(promise=>{
promise.then((value,index)=>{
res[idx]=value;//解析promise里面的值
if(res.length==promises.length){
resolve(res)
}
},reason=>reject(reason))
})
})
}

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 function runAsync (x) {
const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
return p
}
function runReject (x) {
const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x))
return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
.then(res => console.log(res))
.catch(err => console.log(err))
// 1s后输出
1
3
// 2s后输出
2
Error: 2
// 4s后输出
4

Promise.race():

只有第一个决议的promise取胜,并且决议结果成为返回promise的决议。其他任务虽然会继续进行,但是race已经不管那些任务的结果了,不能传入空数组,传入空数组promise.race()会挂住,且永远不会决议

1
2
3
4
5
6
7
8
9
10
11
function runAsync (x) {
const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
return p
}
Promise.race([runAsync(1), runAsync(2), runAsync(3)])
.then(res => console.log('result: ', res))
.catch(err => console.log(err))
//1
'result: ' 1
2
3

Promise.race()原理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Promise.race=function(promises){
promises=Array.from(promises);
return new Promise((resolve,reject)=>{
if(promises.length===0){
return;//空数组挂起
}
else{
for(let i=0;i<promises.length;i++){
Promise.resolve(promises[i]).then((data)=>{
resolve(data);
return;//只会返回第一个先resolve的,其他resolve的race不管
},(err)=>{
reject(err);
return;
})
}
}

})
}

Promise.resolve()原理

1
2
3
4
5
6
7
8
9
10
11
12
13
Promsie.resolve=function(param){
if(param instanceof Promise)return param;//如果param本身是promise,则直接返回
return new Promise((resolve,reject)=>{
if(param &&typeof param==='object'&& typeof param.then==='function'){//如果param是个对象并且param是thenable
setTimeout(()=>{//则返回的promise会跟随这个thenable的对象,采用最终状态
param.then(resolve,reject);
})

}else{
resolve(param)//如果param是值,则直接以该值为成功状态返回
}
})
}

Promise.reject()

1
2
3
4
5
Promise.reject=function(reason){
return new Promise((resolve,reject)=>{
return reject(reason)
})
}

Promise.catch()

1
2
3
Promsie.prototype.catch=function(onRejected){
return this.then(null,onRejected);//catch后可以继续.then因此调用this.then后可以返回一个新的promise继续.then
}

Promise.finally():

1
2
3
4
5
6
7
8
9
10
11
Promise.prototype.finally=function(cb){
return this.then((value)=>{
return Promise.resolve(callback).then(()=>{
return value;//finally后可以继续.then因此调用this.then后可以返回一个新的promise继续.then
})
},(err)=>{
return Promise.resolve(callback).then(()=>{
throw err;
})
})
}

每隔一秒打印1,2,3

1
2
3
const arr=[1,2,3];
arr.reduce(
(p,x)=>p.then(()=>new Promise(resolve=>setTimeout(()=>resolve(console.log(x)),1000))),Promise.resolve())

一秒后打印出1,2,3

1
2
3
arr.reduce(
(p,x)=>p.then(new Promise(resolve=>setTimeout(resolve(console.log(x)),1000))),Promise.resolve()
)

红绿灯交替闪烁

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
30
function red(){
console.log("red")
}
function yellow(){
console.log("yellow")
}
function green(){
console.log("green")
}
const light=(time,cb)=>{
return new Promise(resolve=>{
setTimeout(()=>{
cb();
resolve();
},time)
})

}
const step=function(){
Promise.resolve()
.then(()=>{
return light(3000,red)
})
.then(()=>{
return light(2000,yellow)
})
.then(()=>{
return light(1000,green)
})}
step();

mergepromise

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
30
31
32
33
34
35
36
const time = (timer) => {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, timer)
})
}
const ajax1 = () => time(2000).then(() => {
console.log(1);
return 1
})
const ajax2 = () => time(1000).then(() => {
console.log(2);
return 2
})
const ajax3 = () => time(1000).then(() => {
console.log(3);
return 3
})

function mergePromise (promises) {
// 在这里写代码
let data=[]
let promise=Promise.resolve();
promises.forEach(ajax=>{
//第一次then是调用ajax,第二次then是解析ajax的结果
promise=promise.then(ajax).then(res=>{data.push(res);return data})//把每次结果返回
})
return promise;

}

mergePromise([ajax1, ajax2, ajax3]).then(data => {
console.log("done");
console.log(data); // data 为 [1, 2, 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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
function loadImage(url){
return new Promise((resolve,reject)=>{
const img=new Image();
img.onload=function(){
console.log("一张图片加载完成")
resolve(img)
}
img.onerror=function(){
reject(new Error("Cannot load"+url));
}
img.src=url;
})
}
var urls = [
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting1.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting2.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting3.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting4.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/AboutMe-painting5.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn6.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn7.png",
"https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/bpmn8.png",
];
function limitedLoad(urls,handler,limit){
let sequence=[...urls];
let promises=sequence.splice(0,limit).map((url,index)=>{
return handler(url).then(index=>{
return index;//调用loadImage返回最先加载完成的那张照片下标
})
})
return sequence.reduce(
(p,url)=>{
return p.then(()=>{return Promise.race(promises)})//得到第一张加载完成的图片的下标
.then(fastIndex=>{promises[fastIndex]=handler(url).then(()=>{return fastIndex})})//将已经加载完成的照片的位置替换为为加载的照片
.catch(err=>{console.error(err)})
},Promise.resolve())//初始为Promise.resolve()
.then(()=>{return Promise.all(promises)})//最后3个用Promise.all加载


}
limitedLoad(urls,loadImage,3)
.then(res=>{
console.log("图片全部加载完成")
console.log(res)
})
.catch(err=>{
console.error(err)
})

判断图片是否加载延迟

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
function loadImage(url){
return new Promise((resolve,reject)=>{
const img=new Image();
img.onload=function(){
console.log("一张图片加载完成")
resolve(img)
}
img.onerror=function(){
reject(new Error("Cannot load"+url));
}
img.src=url;
})
}
function timeout(){
var p=new promise((resolve,reject)=>{
setTimeout(()=>{
reject('图片超时')
},5000)
})
}
Promise.race([loadImage(),timeout()]).then((data)=>{
console.log(data);
}).catch((err)=>{
console.log(err)
})