javascript - เงื่อนไขการแข่งขันเมื่อต้องใช้ฟังก์ชัน _.once ของ Lodash
ตัวอย่างเช่น พิจารณาโค้ดต่อไปนี้:
const fetch = _.once(myRealFetch)
const queue = new PQueue({concurrency: 1000});
queue.add(function() {
const result = fetch()
// Rest of the code ...
})
queue.add(function() {
const result = fetch()
// Rest of the code ...
})
await queue.start().onIdle();
มันประกาศฟังก์ชัน fetch ซึ่งถูกเมมโมไทซ์ให้ทำงานเพียงครั้งเดียวโดยใช้ฟังก์ชัน _.once ของ Lodash
จากนั้นฉันจึงสร้าง PQueue ต่อ เพิ่มฟังก์ชัน 2 ฟังก์ชันที่เรียกใช้ fetch เข้าไปในคิว และรันมัน
ตอนนี้สมมติว่า myRealFetch ใช้เวลา 5 วินาทีในการทำงาน เนื่องจากฉันเรียกมันด้วย concurrency > 1 นั่นหมายความว่าหนึ่งในสองฟังก์ชันจะเรียกใช้งานมันก่อน และมีโอกาสสูงว่าฟังก์ชันที่สองก็จะเรียกใช้งานมันด้วยเช่นกัน
ในตอนนี้ การเรียกใช้งานครั้งแรกยังไม่เสร็จ ดังนั้นสิ่งที่จะถูกส่งกลับในการเรียกใช้งานครั้งที่สองคืออะไร?
มีวิธีในตัวเพื่อจัดการกับเรื่องนั้นหรือไม่?
คำตอบ #1
คุณสามารถทดสอบสิ่งนี้ด้วยตัวเองด้วยบางสิ่งเช่นโค้ดด้านล่าง นี่คือ JSFiddle ที่คุณสามารถเล่นเพื่อดูว่าจะเกิดอะไรขึ้น
ฉันได้เพิ่มบันทึกบางส่วนพร้อมเวลาประทับ และยังได้เพิ่มรายการอื่นลงในคิวหลังจากงาน 2 งานแรกเสร็จสิ้น (ประมาณ 6s)
import { once } from 'https://esm.run/lodash-es';
import PQueue from 'https://esm.run/p-queue';
import moment from 'https://esm.run/moment';
const time = moment()
function logWithTime(msg) {
console.log(`[T + ${moment().diff(time, 'ms')}ms]`, msg)
}
const wait = (t) => async () => new Promise((resolve) => {
logWithTime(`Waiting for ${t / 1000}s...`);
setTimeout(() => {
logWithTime(`Done - Waited for ${t / 1000}s`);
resolve('some result');
}, t);
});
const fetch = once(wait(5000))
const queue = new PQueue({ concurrency: 1000 });
queue.add(async function() {
logWithTime('adding 1')
const result = await fetch()
logWithTime('add1 - result')
logWithTime(result)
})
queue.add(async function() {
logWithTime('adding 2')
const result = await fetch()
logWithTime('add2 - result')
logWithTime(result)
})
setTimeout(() => {
logWithTime('adding 3')
queue.add(async function() {
const result = await fetch()
logWithTime('add3 - result')
logWithTime(result)
})
}, 6000)
(async () => {
await queue.start().onIdle();
})()
ผลลัพธ์ที่เกิดจากสิ่งนี้จะเป็น...
[T + 0ms] adding 1
[T + 0ms] Waiting for 5s...
[T + 0ms] adding 2
[T + 5001ms] Done - Waited for 5s
[T + 5002ms] add1 - result
[T + 5002ms] some result
[T + 5002ms] add2 - result
[T + 5002ms] some result
[T + 6002ms] adding 3
[T + 6002ms] add3 - result
[T + 6002ms] some result
คุณจะเห็นว่าคู่แรก 2 คู่ถูกแก้ไขประมาณ 5 วินาทีเกือบจะทันทีหลังจากกัน ดังนั้น fetch จึงถูกเรียกเพียง ครั้งเดียว สิ่งนี้ยังได้รับการยืนยันจากบันทึกภายในฟังก์ชัน wait ที่ถูกเรียกเพียงครั้งเดียวเช่นกัน
จากนั้นงานที่ 3 ซึ่งถูกเพิ่มหลังจากงานสองงานแรกได้รับการแก้ไขแล้ว จะถูกส่งกลับทันทีโดยไม่เรียกฟังก์ชัน fetch (เช่น wait) ดังนั้น _.once จะถูกใช้งานและการเรียกใช้ฟังก์ชัน fetch อื่นๆ จะถูกส่งกลับทันทีด้วยผลลัพธ์ที่จำค่าเดิมไว้
