Handling concurrency gracefully

有一天,你想知道你家的小猫咪kakiki整天出去厮混的朋友有谁?
首先,你得知道你家猫咪的唯一标识id,然后再用这个id去数据库查它的朋友。
因此,你得发送两个请求,其中第一个请求结果是第二请求的参数。
相信,很多人都遇到过类似这样的情景吧!甚至可能需要发送五六个请求。怎么解决呢?


我们先来弄个假的数据库,用setTimeout来假装网络延迟(@ο@)

Make a fake db

class FakeDatabase{
constructor(){
    this.purr = {
        id: 1,
        name: 'kakiki'
    };
    this.purrs = [this.purr, this.purr, this.purr];
}
getPurr(){
    return new Promise((resolve, reject) => {
        setTimeout(()=>resolve(this.purr), 233);
    })
}
getPurrs(purrId){
    return new Promise((resolve, reject) => {
        setTimeout(()=>resolve(this.purrs.slice()), 233);
    })
}
throwError(){
    return new Promise((resolve, reject) => {
        setTimeout(()=>reject(new Error('Internal error')), 233);
    })
}
}
const db = new FakeDatabase();
let purr, purrs;

用回调是怎样写的呢?

In callback way

function callbackHell(){
db.getPurr().then((returnedPurr)=>{
    purr = returnedPurr;
    db.getPurrs(purr.id).then((returnedPurrs)=>{
        purrs = returnedPurrs;
        console.log("callbackHell ", {purr, purrs});
    })
})
}

写完之后是不是觉得有点恶心,那我们用promise吧
(紫霞仙子,你还记得那个一万年期限的承诺吗/(ㄒoㄒ)/~~)

In promise chain way

function promiseChain(){
db.getPurr()
    .then((returnedPurr)=>{
        purr = returnedPurr;
        return db.getPurrs(purr.id);
    })
    .then((returnedPurrs)=>{
        purrs = returnedPurrs;
        console.log("promiseChain ", {purr, purrs});
    })
}

写完之后,觉得还是不够优雅。用async/await可以假装成是串行。(记得用babel来兼容老顽固😯)

In asyncAwait way

async function asyncAwait(){
try{
    purr = await db.getPurr();
    purrs = await db.getPurrs(purr.id);
    console.log("asyncAwait ", {purr, purrs});
    //循环遍历
    const purrsPromises = purrs.map(purr => db.getPurrs(purr.id));
    const morePurrs = await Promise.all(purrsPromises);
    console.log('morePurrs ', morePurrs);
}catch(err){
    //异常流
    console.error(err);
}
}

现在终于知道kakiki的死党是谁了!!
而async/await又是怎么从promise、generator逐步演变过来的呢?
咱们下回分解。

Reference

what-is-async-await-why-should-you-care

`