83

用 Promise 讲一个悲伤的故事给你听

 5 years ago
source link: http://www.10tiao.com/html/780/201807/2650587545/1.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

小编有话说:悲剧 总是更容易被人记住,所以今天用讲故事的方式,来简单的讲一下promise的使用~本文由banxi发表在掘金,已授权奇舞周刊转载

那天我正在学习 Promise,突然家里打电话过来说,家里盖房子要钱。我工作这么多年了,从事着别人眼中高薪工作,于是满口答应下来。但是由于我并没有钱,于是我跟家里说,等过几天我再打钱过去。我也好乘着这几天想想办法。

首先我找到我的同学李雷,他现在一个部门经理了,我想应该他应该有钱。我跟他说明了借钱的意向,李雷二话不说就答应借我300,不过同时表示要回家跟老婆商量商量,我说好。此时我想起来答应或者说承诺的英文单词就是 Promise。承诺的结果是钱,钱是数值(number 类型)。于是我想把我要借钱的这一行为写成一个TypeScript 函数如下:

// 向李雷借钱,李雷丢给我一个承诺

function borrowMoneyFromLiLei(): Promise<number> {

  return new Promise<number>(function(fulfill, reject) {

     // 李雷跟老婆商量中

  });

}

此时,我在想李雷老婆会答应给我借300块吗?我不确定,就像薛定谔的猫。借还是不借,这是一个问题。然后我发现这也可以写成一个函数。借或者不借用布尔值来表示 (boolean 类型)。函数如下:

// 李雷的老婆是否会答应给我借钱?

function willLiLeiWifeLendMeMoeny(): Promise<boolean> {

  return new Promise<boolean>(function(lend, reject) {

    // 借还是不借

  });

}

如果李雷借我钱了,我就转钱给家里,没有,我应该要再去找别人借了。可以用下面的函数描述我此时的处境。

function transferMoneyToHome(money: number) {

    // 给家里转钱

}

function mySituation(){

    borrowMoneyFromLiLei()

    .then((money:number) => {

        // 如果李雷借我钱了,我就转钱给家里.

        transferMoneyToHome(money)

    }).catch((reason) => {

        // 李雷老婆拒绝借钱给我。 那我应该考虑向其他人借了。

        borrowMoneyFromOthers()

    })

}

找其他人借,我能想到就(张三,李四,王五)这三个人了,其他的朋友很少联系,突然说借钱也不好。于是我尝试向他们借钱。用代码表示是这样子的:

function borrowMoneyFromOthers() {

  // 我先试着向张三借

  tryBorrowMoneyFromZhangshan()

    .then(money => {

      transferMoneyToHome(money);

    })

    .catch(reason => {

      // 如果张三不借,并丢给我一个理由

      // 试着向李四借

      tryBorrowMoneyFromLisi()

        .then(money => {

          transferMoneyToHome(money);

        })

        .catch(reason2 => {

          // 如果 李四也不肯错

          // 再试试向王五借

          tryBorrowMoneyFromWangwu()

            .then(money => {

              transferMoneyToHome(money);

            })

            .catch(reason => {

              // 没有人肯借

              throw new Error("我该怎么办呢?");

            });

        });

    });

}

由于借着钱之后都是向家里转钱,所以上面的代码应该简化一下。简化后如下:

function borrowMoneyFromOthers() {

  // 我先试着向张三借

  tryBorrowMoneyFromZhangshan()

    .then(transferMoneyToHome)

    .catch(reason => {

      // 如果张三不借,并丢给我一个理由

      // 试着向李四借

      tryBorrowMoneyFromLisi()

        .then(transferMoneyToHome)

        .catch(reason2 => {

          // 如果 李四也不肯错

          // 再试试向王五借

          tryBorrowMoneyFromWangwu()

            .then(transferMoneyToHome)

            .catch(reason => {

              // 没有人肯借

              throw new Error("我该怎么办呢?");

            });

        });

    });

}

在上面的思路中,我是一个一个找他们借钱的,一个借不着再找另一个。我为什么不同时找他们借呢?谁借我了,我就转钱给家里。此时我想起了刚学的Promise.race 方法,也许这个方法可以帮助我表达我的这一决策需求.

function borrowMoneyFromOthers() {

  // 同时向张三,李四,王五借钱,只要有人借我钱了,我就转钱给家里。

  Promise.race([

    tryBorrowMoneyFromZhangshan(),

    tryBorrowMoneyFromLisi(),

    tryBorrowMoneyFromWangwu()

  ])

    .then(transferMoneyToHome)

    .catch(reasons => {

      console.warn("没一个人愿意给我借钱,他们理由是:", reasons);

    });

}

我用timeout 模拟一下他们给我答复的,代码如下:

// 尝试找张三借

function tryBorrowMoneyFromZhangshan(): Promise<number> {

  return new Promise(function(fulfill, reject) {

    setTimeout(() => {

      fulfill(300);

    }, 100);

  });

}

// 尝试找李四借

function tryBorrowMoneyFromLisi(): Promise<number> {

  return new Promise(function(fulfill, reject) {

    setTimeout(() => {

      reject("对不起我也没钱");

    }, 50);

  });

}

// 尝试找王五借

function tryBorrowMoneyFromWangwu(): Promise<number> {

  return new Promise(function(fulfill, reject) {

    setTimeout(() => {

      fulfill(300);

    }, 500);

  });

}

结果运行之后,控制台输出的是:

没一个人愿意给我借钱,他们理由是: 对不起我也没钱

看来 Promise.race 适用用来模拟抢答,而不是选择最优解。 比如多人抢答一个问题,第一个抢答之后不论他回答的是否是正确,这个题都过了。

不过没关系。也许我可以自己写一个来叫做 promiseOne 的函数来实现这个功能。代码如下:

/**

 * 当其中一个 Promise 兑现时,返回的 Promise 即被兑现

 * @param promises Promise<T> 的数组

 */

function promiseOne<T>(promises: Promise<T>[]): Promise<T> {

  const promiseCount = promises.length;

  return new Promise<T>(function(resolve, reject) {

    const reasons: any[] = [];

    let rejectedCount = 0;

    promises.forEach((promise, index) => {

      promise.then(resolve).catch(reason => {

        reasons[index] = reason;

        rejectedCount++;

        if (rejectedCount === promiseCount) {

          reject(reasons);

        }

      });

    });

  });

}

正当我写完了上面的代码,他们三个给我回话了,说是现在手上也没有那么多钱,但是可以给我借100. 于是我现在需要处理这样的事情,就是当他们三个人把钱都转给我之后我再转给家里。 当他们三个都兑换借我100块钱的承诺时,可以用 Promise.all 来表示,代码如下:

function borrowMoneyFromOthers() {

  // 同时向张三,李四,王五借钱, 借到之后,我就转钱给家里。

  Promise.all([

    tryBorrowMoneyFromZhangshan(),

    tryBorrowMoneyFromLisi(),

    tryBorrowMoneyFromWangwu()

  ])

    .then(moneyArray => {

      console.info("借到钱啦:", moneyArray);

      const totalMoney = moneyArray.reduce((acc, cur) => acc + cur);

      transferMoneyToHome(totalMoney);

    })

    .catch(reasons => {

      console.warn("有人不愿意给我借钱,理由是:", reasons);

    });

}

现在有三个人愿意给我借钱了,嗯,也就是说我借到了 300 块。然而这钱用来建房还是杯水车薪。所以我还得想办法。我想我要不要试试用这300块来买一下彩票。如果中了,说不定这事就成了。

function buyLottery(bet: number): Promise<number> {

  return new Promise(function(fulfill, resolve) {

    // 投注

    // 等待开奖

    setTimeout(() => {

      resolve("很遗憾你没有买中");

    }, 100);

  });

}


function borrowMoneyFromOthers() {

  // 同时向张三,李四,王五借钱,

  Promise.all([

    tryBorrowMoneyFromZhangshan(),

    tryBorrowMoneyFromLisi(),

    tryBorrowMoneyFromWangwu()

  ])

    .then(moneyArray => {

      console.info("借到钱啦:", moneyArray);

      const totalMoney = moneyArray.reduce((acc, cur) => acc + cur);

      // 购买彩票

      buyLottery(totalMoney)

        .then(transferMoneyToHome)

        .catch(reason => {

          console.log("没中,", reason);

        });

    })

    .catch(reasons => {

      console.warn("有人不愿意给我借钱,理由是:", reasons);

    });

}

我知道很大概率我是买不中的,最近世界杯开赛了,我幻想着压注世界杯,而且世界杯场次多,一天好几场,一场买中的盈利还可以投入到下一场。我把我的幻想写成代码,大概就是下面这样。

function betWorldCup() {

  // 初始资金 300 块

  Promise.resolve(300)

    .then(moeny => {

      // 投西班牙

      return new Promise<number>(function(fulfil, reject) {

        setTimeout(() => {

          // 假假设 赔率 1.2

          fulfil(moeny * 1.2);

        }, 100);

      });

    })

    .then(ret => {

      // 投英格兰

      return ret * 1.2;

    })

    .then(ret => {

      // 投巴西

      return new Promise<number>(function(fulfil, reject) {

        setTimeout(() => {

          fulfil(ret * 1.2);

        }, 92);

      });

    })

    .then(ret => {

      console.log("现在收益加本金共有: ", ret);

    });

}

我想,如果第一场投失败了,应该再给自己一次机会。于是将代码修改如下:

function betWorldCup() {

  // 初始资金 300 块

  Promise.resolve(300)

    .then(moeny => {

      // 投西班牙

      return new Promise<number>(function(fulfil, reject) {

        setTimeout(() => {

          // 假假设 赔率 1.2

          // fulfil(moeny * 1.2);

          reject("庄家跑跑路了");

        }, 100);

      });

    })

    .then(

      ret => {

        // 投英格兰

        return ret * 1.2;

      },

      reason => {

        console.info("第一次投注失败,再给一次机会好不好?, 失败原因: ", reason);

        // 再投 300

        return 300;

      }

    )

    .then(ret => {

      // 投巴西

      return new Promise<number>(function(fulfil, reject) {

        setTimeout(() => {

          fulfil(ret * 1.2);

        }, 92);

      });

    })

    .then(ret => {

      console.log("现在收益加本金共有: ", ret);

      throw new Error("不要再买了");

    })

    .then(ret => {

      console.info("准备再买吗?");

    })

    .catch(reason => {

      console.log("出错了:", reason);

    });

}

此时如下运行上面的函数会得到如下输出:

第一次投注失败,再给一次机会好不好?, 失败原因: 庄家跑跑路了

现在收益加本金共有: 360

出错了:

Error: 不要再买了

然而,幻想结束之后,我依然得苦苦思考怎么样筹钱。

关于奇舞周刊

《奇舞周刊》是360公司专业前端团队「奇舞团」运营的前端技术社区。关注公众号后,直接发送链接到后台即可给我们投稿。



About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK