那天我正在學習 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: 不要再買了
複製程式碼
然而,幻想結束之後,我依然得苦苦思考怎麼樣籌錢。