การรวมกันของฟังก์ชั่น async + รอ + setTimeout


305

ฉันกำลังพยายามใช้คุณสมบัติใหม่ของ async และฉันหวังว่าการแก้ปัญหาของฉันจะช่วยผู้อื่นในอนาคต นี่คือรหัสของฉันซึ่งใช้งานได้:

  async function asyncGenerator() {
    // other code
    while (goOn) {
      // other code
      var fileList = await listFiles(nextPageToken);
      var parents = await requestParents(fileList);
      // other code
    }
    // other code
  }

  function listFiles(token) {
    return gapi.client.drive.files.list({
      'maxResults': sizeResults,
      'pageToken': token,
      'q': query
    });
  }

ปัญหาคือขณะที่ลูปของฉันทำงานเร็วเกินไปและสคริปต์ส่งคำขอมากเกินไปต่อวินาทีไปยัง google API ดังนั้นฉันต้องการที่จะสร้างฟังก์ชั่นการนอนหลับซึ่งล่าช้าคำขอ ดังนั้นฉันสามารถใช้ฟังก์ชั่นนี้เพื่อชะลอการร้องขออื่น ๆ หากมีวิธีอื่นในการหน่วงเวลาการร้องขอโปรดแจ้งให้เราทราบ

อย่างไรก็ตามนี่คือรหัสใหม่ของฉันซึ่งใช้งานไม่ได้ การตอบสนองของคำขอจะถูกส่งกลับไปยังฟังก์ชัน async แบบไม่ระบุชื่อภายใน setTimeout แต่ฉันไม่รู้ว่าฉันจะคืนการตอบสนองไปยังฟังก์ชัน sleep ได้อย่างไร ไปยังฟังก์ชัน asyncGenerator เริ่มต้น

  async function asyncGenerator() {
    // other code
    while (goOn) {
      // other code
      var fileList = await sleep(listFiles, nextPageToken);
      var parents = await requestParents(fileList);
      // other code
    }
    // other code
  }

  function listFiles(token) {
    return gapi.client.drive.files.list({
      'maxResults': sizeResults,
      'pageToken': token,
      'q': query
    });
  }

  async function sleep(fn, par) {
    return await setTimeout(async function() {
      await fn(par);
    }, 3000, fn, par);
  }

ฉันได้ลองใช้ตัวเลือกแล้ว: การจัดเก็บการตอบสนองในตัวแปรทั่วโลกและส่งคืนจากฟังก์ชั่นสลีป, โทรกลับภายในฟังก์ชั่นที่ไม่ระบุชื่อ ฯลฯ

คำตอบ:


614

sleepฟังก์ชั่นของคุณไม่ทำงานเพราะsetTimeoutยังไม่ได้คืนสัญญาที่สามารถแก้ไขawaitได้ คุณจะต้องแนะนำด้วยตนเอง:

function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
async function sleep(fn, ...args) {
    await timeout(3000);
    return fn(...args);
}

Btw เพื่อทำให้วงของคุณช้าลงคุณอาจไม่ต้องการใช้sleepฟังก์ชั่นที่ใช้ในการโทรกลับและทำให้มันเป็นแบบนี้ ฉันอยากจะแนะนำให้ทำอะไรบางอย่างเช่น

while (goOn) {
  // other code
  var [parents] = await Promise.all([
      listFiles(nextPageToken).then(requestParents),
      timeout(5000)
  ]);
  // other code
}

ซึ่งช่วยให้การคำนวณparentsใช้เวลาอย่างน้อย 5 วินาที


11
รักPromise.allวิธีการ เรียบง่ายและสง่างาม!
Anshul Koka

4
สัญลักษณ์ของการvar [parents]เป็นตัวแทนคืออะไร? ฉันไม่เคยเห็นมาก่อนและเป็นเรื่องยากสำหรับ google
natedog


1
@tinkerr "การหมดเวลาจะต้องมีการประกาศเป็น async หากจำเป็นต้องรอ " - Nope ฟังก์ชั่นจะต้องส่งคืนสัญญาที่สามารถรอได้ (หรือจริงๆแล้วก็เพียงพอแล้ว) async functionวิธีการที่จะประสบความสำเร็จที่ขึ้นอยู่กับการดำเนินงานของฟังก์ชั่นที่มันไม่จำเป็นต้องเป็น
Bergi

2
@naisanza ไม่async/ awaitจะขึ้นอยู่กับสัญญา สิ่งเดียวที่เข้ามาแทนที่คือการthenโทร
Bergi

150

ตั้งแต่โหนด 7.6คุณสามารถรวมฟังก์ชั่นpromisifyฟังก์ชั่นจากโมดูล utils setTimeout()กับ

Node.js

const sleep = require('util').promisify(setTimeout)

จาวาสคริ

const sleep = m => new Promise(r => setTimeout(r, m))

การใช้

(async () => {
    console.time("Slept for")
    await sleep(3000)
    console.timeEnd("Slept for")
})()

1
ใน nodeJS await require('util').promisify(setTimeout)(3000)ยังสามารถทำได้โดยไม่ต้องจำเป็นต้องมีโดย:await setTimeout[Object.getOwnPropertySymbols(setTimeout)[0]](3000)
Shl

5
@Shl ที่น่าสนใจ ฉันคิดว่ามันอ่านได้น้อยกว่าโซลูชันของฉัน หากคนไม่เห็นด้วยฉันสามารถเพิ่มลงในโซลูชันได้หรือไม่
แฮร์รี่

2
รุ่นที่ต้องการนั้นชัดเจนกว่าgetOwnPropertySymbolsรุ่นอื่น ๆ ... ถ้ามันยังไม่พัง ... !
Matt Fletcher

2
เฮ้มี @Harry ดูเหมือนว่าคุณได้รวมซับหนึ่งจากคำตอบของ FlavourScape ในคำตอบของคุณเอง ฉันไม่ต้องการที่จะเข้าใจความตั้งใจของคุณ แต่นั่นไม่ยุติธรรมกับพวกเขา คุณสามารถย้อนกลับการแก้ไขของคุณ? ตอนนี้ดูเหมือนว่าจะเป็นการขโมยความคิด ..
Félix Gagnon-Grenier

2
ฉันได้ลบหนึ่งบรรทัดออกไปเนื่องจากคำตอบอยู่ด้านล่าง แต่ฉันได้เห็นคำตอบยอดนิยมมากมายอัปเดตคำตอบของพวกเขาเพื่อรวมคำตอบใหม่อื่น ๆ เนื่องจากผู้อ่านส่วนใหญ่ไม่ใส่ใจในการตอบสนองสองสามข้อแรก
Harry

130

สายการบินเดียวแบบอินไลน์รวดเร็ว

 await new Promise(resolve => setTimeout(resolve, 1000));

4
let sleep = ms => new Promise( r => setTimeout(r, ms));// หนึ่งฟังก์ชันซับ
Soldeplata Saketos

8
ยิ่งสั้น :-)await new Promise(resolve => setTimeout(resolve, 5000))
Liran Brimer

1
หมายความว่าอย่างไรเมื่อพวกคุณใช้ "แก้ไข" x 2 ครั้งในบรรทัดเดียวกัน ไลค์: รอสัญญาใหม่ (แก้ไข => setTimeout (แก้ไข, 1,000)); มันไม่อ้างอิง เพื่อตัวเองหรืออะไร ฉันจะทำสิ่งนี้แทน: function myFunc () {}; รอการสัญญาใหม่ (แก้ไข => setTimeout (myFunc, 1,000));
PabloDK

35

setTimeoutไม่ใช่asyncฟังก์ชั่นดังนั้นคุณไม่สามารถใช้กับ ES7 async-await แต่คุณสามารถใช้sleepฟังก์ชั่นของคุณโดยใช้สัญญา ES6 :

function sleep (fn, par) {
  return new Promise((resolve) => {
    // wait 3s before calling fn(par)
    setTimeout(() => resolve(fn(par)), 3000)
  })
}

จากนั้นคุณจะสามารถใช้sleepฟังก์ชั่นใหม่นี้กับ ES7 async-await:

var fileList = await sleep(listFiles, nextPageToken)

โปรดทราบว่าฉันเพียงตอบคำถามของคุณเกี่ยวกับการรวม async ES7 / รอด้วยsetTimeoutถึงแม้ว่ามันอาจไม่ช่วยแก้ปัญหาของคุณด้วยการส่งคำขอมากเกินไปต่อวินาที


อัพเดต:เวอร์ชัน node.js ที่ทันสมัยมีการใช้งานการหมดเวลา async ของ buid ซึ่งสามารถเข้าถึงได้ผ่านตัวช่วยutil.promisify :

const {promisify} = require('util');
const setTimeoutAsync = promisify(setTimeout);

2
คุณไม่ควรทำอย่างนั้นเมื่อfnพ่นความผิดพลาดออกไปจะไม่ถูกจับ
Bergi

@Bergi ฉันคิดว่ามันฟองขึ้นไปที่new Promiseที่คุณสามารถsleep.catchมัน
Florian Wendelborn

3
@Dodekeract ไม่มันอยู่ในการsetTimeoutติดต่อกลับแบบอะซิงโครนัสและการnew Promiseโทรกลับได้ดำเนินการมานานแล้ว มันจะกระทบกับบริบทของโลกและถูกโยนเป็นข้อยกเว้นที่ไม่สามารถจัดการได้
Bergi

> ปัญหาเกี่ยวกับการส่งคำขอมากเกินไปต่อวินาที คุณต้องการใช้ "debounce" บางทีเพื่อป้องกันสิ่งต่าง ๆ เช่น UI ที่เผาทำลายจำนวนมากเกินไป
FlavourScape

5

หากคุณต้องการใช้ไวยากรณ์แบบเดียวกับที่setTimeoutคุณสามารถเขียนฟังก์ชันตัวช่วยได้ดังนี้

const setAsyncTimeout = (cb, timeout = 0) => new Promise(resolve => {
    setTimeout(() => {
        cb();
        resolve();
    }, timeout);
});

จากนั้นคุณสามารถเรียกมันว่า:

const doStuffAsync = async () => {
    await setAsyncTimeout(() => {
        // Do stuff
    }, 1000);

    await setAsyncTimeout(() => {
        // Do more stuff
    }, 500);

    await setAsyncTimeout(() => {
        // Do even more stuff
    }, 2000);
};

doStuffAsync();

ฉันทำรายการสำคัญ: https://gist.github.com/DaveBitter/f44889a2a52ad16b6a5129c39444bb57


1
ชื่อฟังก์ชั่นที่ต้องการdelayRunจะสมเหตุสมผลมากขึ้นที่นี่เนื่องจากมันจะชะลอการเรียกใช้ฟังก์ชันการเรียกกลับภายในสิบวินาที ไม่ใช่ตัวอย่างที่รอคอยอย่างมาก IMO
mix3d

2
var testAwait = function () {
    var promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('Inside test await');
        }, 1000);
    });
    return promise;
}

var asyncFunction = async function() {
    await testAwait().then((data) => {
        console.log(data);
    })
    return 'hello asyncFunction';
}

asyncFunction().then((data) => {
    console.log(data);
});

//Inside test await
//hello asyncFunction

0

รหัสต่อไปนี้ใช้งานได้ใน Chrome และ Firefox และเบราว์เซอร์อื่น ๆ

function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
async function sleep(fn, ...args) {
    await timeout(3000);
    return fn(...args);
}

แต่ใน Internet Explorer ฉันได้รับข้อผิดพลาดทางไวยากรณ์สำหรับ "(resolve **=>** setTimeout..."


0

สร้างแรงบันดาลใจจากคำตอบของเดฟ

ส่งผ่านโดยทั่วไปในการdoneโทรกลับเพื่อโทรเมื่อดำเนินการเสร็จ

// Function to timeout if a request is taking too long
const setAsyncTimeout = (cb, timeout = 0) => new Promise((resolve, reject) => {
  cb(resolve);
  setTimeout(() => reject('Request is taking too long to response'), timeout);
});

นี่คือวิธีที่ฉันใช้:

try {
  await setAsyncTimeout(async done => {
    const requestOne = await someService.post(configs);
    const requestTwo = await someService.get(configs);
    const requestThree = await someService.post(configs);
    done();
  }, 5000); // 5 seconds max for this set of operations
}
catch (err) {
  console.error('[Timeout] Unable to complete the operation.', err);
}

0

นี่คือรุ่นของฉันที่มี nodejs ในปี 2020 ในแล็บ AWS ของ AWS

const sleep = require('util').promisify(setTimeout)

async function f1 (some){
...
}

async function f2 (thing){
...
}

module.exports.someFunction = async event => {
    ...
    await f1(some)
    await sleep(5000)
    await f2(thing)
    ...
}

-3

นี่คือการแก้ไขที่รวดเร็วยิ่งขึ้นในหนึ่งซับ

หวังว่าจะช่วยได้

// WAIT FOR 200 MILISECONDS TO GET DATA //
await setTimeout(()=>{}, 200);

1
ใช้งานไม่ได้ สิ่งนี้: await setTimeout(()=>{console.log('first')}, 200); console.log ('second')พิมพ์สองจากนั้นก่อน
gregn3

1
@ gregn3 นั่นคือจุดใช่ นี่เป็นโซลูชันที่ไม่ใช่การปิดกั้นที่โค้ดภายนอกฟังก์ชั่นสามารถดำเนินการต่อในขณะที่ "การดำเนินการปิดกั้น" จะเสร็จสมบูรณ์นอกกระแสหลักของโปรแกรม แม้ว่าไวยากรณ์ที่คุณและ Rommy และ Mohamad ได้ให้ไว้นั้นไม่ถูกต้องเนื่องจากข้อกำหนดสำหรับการรอคอยที่จะได้รับการเคาะในฟังก์ชั่น async (อาจเป็นการเพิ่มที่ค่อนข้างเร็ว ๆ นี้) แต่ฉันก็ใช้ node.js นี่คือวิธีการแก้ปัญหาของฉัน var test = async () => { await setTimeout(()=>{console.log('first')}, 1000); console.log ('second') }ฉันขยายเวลาการหมดเวลาเพื่อแสดงว่ามันมีประโยชน์
azariah
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.