同步、异步和Promise的问题

3,445 阅读6分钟

同步和异步

同步和异步是一种消息通知机制

同步阻塞、同步非阻塞、(异步阻塞、异步非阻塞)(js中:前两种):小明去图书馆找书
  • 小明:决定阻塞非阻塞,站在前台等 —— 阻塞,去干点其他事 —— 非阻塞
  • 图书馆找书的管理员:决定异步同步,找到书有消息通知机制 —— 异步
  • 如果图书馆找书的管理员没有消息通知机制,小明可以轮询
同步: 脑子一根筋,一件事做完再做下一件事:
A 调用B,B处理获得结果,才返回给A。A在这个过程中,一直等待B的处理结果,没有拿到结果之前,需要A(调用者)一直等待和确认调用结果是否返回拿到结果,然后继续往下执行。
异步: 一件事到了等待时间,我们继续做下一件事,不会原地等待上一件事执行完毕:

A调用B,无需等待B的结果,B通过状态,通知等来通知A或回调函数来处理。调用结果返回时,会以消息或回调的方式通知调用者。

js中我们会遇到哪些异步的情况?

  • 定时器 setTimeout
  • 动画帧 requestAnimationFrame
  • 事件绑定
  • 回调函数
  • 数据请求 Ajax / 请求图片
  • ...

event loop (wait...)

宏任务、微任务 (wait...)

回调地狱

function fn(cb){
    setTimeout(()=>{
        console.log("fn...")
        cb && cb();
    },100)
}
// 回调函数 cb
fn(function(){
    console.log(2222);
});

回调嵌套层数过多,代码的结构就必然嵌套层级特别多, 造成可读性和维护性的直线下降

Promise 优化异步写法

resolve 调用 resolve 时:该异步执行完成,并且成功

reject 调用 reject 时:该异步执行完成,但是失败

栗子:

 let p = new Promise((resolve, reject) => {
     setTimeout(()=>{
        reject();  // 找下面第一个回调(从0计数),如果有就调用,如果没有就报错
    },3000);
 }).then(()=>{
    console.log("resolved");   // 成功
 },()=>{
    console.log("rejected");  // 失败
 });

Promise:

三种状态 pending、resolved(fullfilled)成功、rejected

PromiseStatus Promise 中异步处理的当前状态

  • pending 异步执行中
  • resolved(fullfilled) 该 promise 执行完成,并且成功了
  • rejected 该 promise 执行完成,但是出错了

then 两个参数 :onresolved ,onrejected链式操作(可选)

let p = new Promise((resolve,reject)=>{
    // 异步处理
    let img = new Image();
    img.src = "...";
    img.onload = function(){
        resolve(img);
    };
    img.onerror = function(err){
        reject(err);
    };
}); 
// then 代表异步执行完了
p.then((img)=>{
    document.body.appendChild(img);
},(err)=>{
    console.log(err);
})

then 三种返还值

let p1 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve("成功");
    })
})
// 1.没有返还 -> 返还promise对象
let res = p1.then(res=>{
   
})
console.log(res);

let p1 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve("成功");
    })
})
// 2.返还值 -> 包装成promise对象返还
let res = p1.then(res=>{
    return 111;
})
console.log(res);

let p1 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve("成功");
    })
})
// 3.返还promise对象  --> 原封不动返还promise对象;
let res = p1.then(res=>{
    return new Promise(resolve=>{
        resolve("返还的值");
    })
})
console.log(res);

链式调用

p1.then(res=>{
    return new Promise(resolve=>{
        resolve(111)
    });
}).then(res=>{
    console.log(res);  // 111   //promisevalue
}).then(res=>{
    console.log(res); // undefined   //promisevalue
}).catch(err=>{
    console.log(err);
})

new Promise((resolve)=>{
    console.log(1);
    resolve();
}).then(()=>{
    console.log(2);
}).then(()=>{
    console.log(3);
}).then(()=>{
    console.log(4);
});

new Promise((resolve)=>{
    console.log(1);
    resolve(111111);
}).then((res)=>{
    console.log(2, res);
}).then((res)=>{
    console.log(3, res);
}).then((res)=>{
    console.log(4, res);
});

调用promise时候,返回一个新的Promise对象,调用then的时候也会返回新的Promise对象
new Promise((resolve)=>{
    window.onload=function(){
        resolve();
    }
}).then(()=>{
    return new Promise((resolve)=>{
        box.style.width = "200px";
        setTimeout(()=>{
            resolve();
        },1000)
    })
}).then(()=>{
    return new Promise((resolve)=>{
        box.style.height = "200px";
        setTimeout(()=>{
            resolve();
        },1000)
    })
}).then(()=>{
    console.log("动画执行结束");
});
then 的返回值:
    then 方法会返回一个 新的 Promise
        1. 默认情况下 then 会返回状态为 Resolved 的 Promise
        2. 也可以直接在 then 的回调中返回一个 Promise 对象,该 Promise 会覆盖掉 then 默认返回的 Promise

出错的问题

let p = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        // resolve();
        reject("出错1");
    },3000);
}).then(()=>{
    console.log(1);
})
// 没有第二个参数,此时会报一个错误

<!--写上第二个参数后无报错,等于链式调用时,then里都需要写两个回调-->
let p = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        // resolve();
        reject("出错1");
    },3000);
}).then(()=>{
    console.log(1);
}, ()=>{
    console.log(2);
})

<!--catch 方法:
    用来捕获错误,走catch方法
    
    catch 用来捕获错误
        catch 捕获该链式调用中,之前的错误(只能捕获它之前的错误,后面的捕获不到)
        catch 同 then 一样,都会返回一个 Promise 规则 和 then 一样
        
-->        
let p = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        //resolve();
        reject("出错1");  //这里出错则下面俩then都不会执行了,直接跳到catch
    },3000);
}).then(()=>{
    console.log(1);
}).then(()=>{
    console.log(2);
}).catch((err)=>{
    console.log(err);  
    //此处的catch只能捕获上面的错误
    // 捕获错误之后返回一个状态为resolve的Promise

}).then(()=>{
    console.log(3);
}).then(()=>{
    console.log(4);
    return Promise.reject("错误2"); 
    // 直接返回一个状态为 rejected 的 Promise
    // 被下面的catch捕获
    console.log(5);
}).catch((err)=>{
    console.log(err);
})

// 接下来看看没出错时候的执行结果
let p = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve();       //** 只有这里不一样哦 **
        // reject("出错1");
    },3000);
}).then(()=>{
    console.log(1);
}).then(()=>{
    console.log(2);
}).catch((err)=>{
    console.log(err);
}).then(()=>{
    console.log(3);
}).then(()=>{
    console.log(4);
    return Promise.reject("错误2");// 直接返回一个状态为 rejected 的 Promise
    console.log(5);
}).catch((err)=>{
    console.log(err);
})

静态方法,不需要new

Promise 下的方法:resolve、reject、all、race、finally

<!--
# Promise.reject
Promise.reject(reason) 返回一个状态为 Rejected 的 Promise 对象

参数:
    reason 失败原因

# Promise.resolve
Promise.resolve(value) 返回一个状态为 resolved 的 Promise 对象
-->
let res = Promise.resolve("success");
console.log(res);

let res = Promise.reject("error");
console.log(res);

Promise.all

// Promise.all 所有promise对象都成功
// 图片很大,分成小块去请求,全部请求成功后执行
let p = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve();
        console.log(1);
    },1000);
});
let p2 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve();
        console.log(2);
    },2000);
});
let p3 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        reject();
        console.log(3);
    },2000);
});
Promise.all([p,p2,p3]).then(()=>{
    console.log("成功");  // 全部成功
}).catch(()=>{  
    console.log("失败")  // 有一个失败即失败
});

所有都成功输出结果

let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(111);
    }, 2000)
})
let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2222);
    }, 1000)
})

// 等所有promise对象都成功输出
Promise.all([p1,p2]).then(res=>{
    console.log(res);
})

2秒后的输出结果:

有一个失败,则拿不到结果

let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject("error");
    }, 1000)
})
let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2222);
    }, 2000)
})

// 所有promise对象都成功
Promise.all([p1,p2]).then(res=>{
    console.log(res);
})

Promise.race

拿到最先变化状态的那个

// Promise.race 但是其中有一项的状态发生改变新的实例的状态就会随着改变
Promise.race([p,p2,p3]).then(()=>{
    console.log("成功");
}).catch(()=>{
    console.log("失败")
});
let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(111);
        // reject("error");
    }, 1000)
})
let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2222);
    }, 2000)
})

finally

无论成功失败都要走到finally

成功
let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(111);
        // reject("error");
    }, 1000)
})
let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2222);
    }, 2000)
})

// finally :
p1.then(res=>{
    console.log(res)
}).catch(err=>{
    console.log(err);
}).finally(()=>{
    console.log("执行完成");
});

失败
let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        // resolve(111);
        reject("error");
    }, 1000)
})
let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2222);
    }, 2000)
})

// finally :
p1.then(res=>{
    console.log(res)
}).catch(err=>{
    console.log(err);
}).finally(()=>{
    console.log("执行完成");
});