Kết hợp chức năng async + await + setTimeout


306

Tôi đang cố gắng sử dụng các tính năng async mới và tôi hy vọng việc giải quyết vấn đề của mình sẽ giúp ích cho những người khác trong tương lai. Đây là mã của tôi đang hoạt động:

  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
    });
  }

Vấn đề là, vòng lặp while của tôi chạy quá nhanh và tập lệnh gửi quá nhiều yêu cầu mỗi giây đến API của google. Vì vậy, tôi muốn xây dựng một chức năng ngủ làm trì hoãn yêu cầu. Do đó tôi cũng có thể sử dụng chức năng này để trì hoãn các yêu cầu khác. Nếu có một cách khác để trì hoãn yêu cầu, xin vui lòng cho tôi biết.

Dù sao, đây là mã mới của tôi không hoạt động. Phản hồi của yêu cầu được trả về hàm async ẩn danh trong setTimeout, nhưng tôi không biết làm thế nào tôi có thể trả lại phản hồi cho chức năng ngủ. đến chức năng asyncGenerator ban đầu.

  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);
  }

Tôi đã thử một số tùy chọn: lưu trữ phản hồi trong một biến toàn cục và trả về từ hàm ngủ, gọi lại trong hàm ẩn danh, v.v.

Câu trả lời:


615

sleepChức năng của bạn không hoạt động vì setTimeoutchưa (chưa?) Trả lại một lời hứa có thể được chỉnh sửa await. Bạn sẽ cần phải quảng cáo bằng tay:

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

Btw, để làm chậm vòng lặp của bạn, có lẽ bạn không muốn sử dụng sleepchức năng gọi lại và trì hoãn nó như thế này. Tôi muốn giới thiệu để làm một cái gì đó như

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

cho phép tính toán parentsmất ít nhất 5 giây.


11
Yêu Promise.allcách tiếp cận. Thật đơn giản và thanh lịch!
Anshul Koka

4
ký hiệu var [parents]đại diện là gì? Tôi chưa từng thấy nó trước đây và đó là một điều khó khăn với google
natedog

6
@NateUsher Đó là phá hủy mảng
Bergi

1
@tinkerr " thời gian chờ cần phải được khai báo không đồng bộ nếu cần chờ " - Không. Một chức năng chỉ cần trả về một lời hứa có thể được chờ đợi (hoặc thực sự, sau đó có thể là đủ). Làm thế nào nó đạt được đó là tùy thuộc vào việc thực hiện chức năng, nó không cần phải là một async function.
Bergi

2
@naisanza Không, async/ awaitđược dựa trên những lời hứa. Điều duy nhất nó thay thế là thencác cuộc gọi.
Bergi

152

Kể từ Node 7.6 , bạn có thể kết hợp chức promisifynăng chức năng từ mô đun utils với setTimeout().

Node.js

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

Javascript

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

Sử dụng

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

1
Trong nodeJS await require('util').promisify(setTimeout)(3000)cũng có thể đạt được mà không cần yêu cầu bởi:await setTimeout[Object.getOwnPropertySymbols(setTimeout)[0]](3000)
Shl

5
Thú vị @Shl. Tôi nghĩ rằng nó là ít đọc hơn giải pháp của tôi mặc dù. Nếu mọi người không đồng ý tôi có thể thêm nó vào giải pháp?
Harry

2
Phiên bản yêu cầu rõ ràng tốt hơn nhiều so với getOwnPropertySymbolsphiên bản ... nếu nó không bị hỏng ...!
Matt Fletcher

2
Xin chào @Harry. Có vẻ như bạn đã kết hợp một lớp lót từ câu trả lời của FlavorScape trong câu trả lời của riêng bạn. Tôi không muốn đoán trước ý định của bạn, nhưng điều đó không thực sự công bằng với họ. Bạn có thể phục hồi chỉnh sửa của bạn? Ngay bây giờ nó trông hơi giống đạo văn ..
Félix Gagnon-Grenier

2
Tôi đã xóa phần một vì câu trả lời ở ngay bên dưới, tuy nhiên tôi đã thấy nhiều câu trả lời phổ biến cập nhật câu trả lời của họ để bao gồm các câu trả lời mới khác vì hầu hết độc giả không bận tâm đến việc trả lời vài câu trả lời đầu tiên.
Harry

130

Một cách nhanh chóng, nội tuyến

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

4
let sleep = ms => new Promise( r => setTimeout(r, ms));// một chức năng lót
Soldeplata Saketos

8
thậm chí ngắn hơn :-)await new Promise(resolve => setTimeout(resolve, 5000))
Liran Brimer

1
Điều đó có nghĩa là gì khi các bạn sử dụng "giải quyết" x 2 lần trong cùng một dòng? Giống như: đang chờ Promise mới (giải quyết => setTimeout (giải quyết, 1000)); nó có tham khảo không với chính nó hay cái gì? Tôi sẽ làm một cái gì đó như thế này thay vào đó: function myFunc () {}; đang chờ Promise mới (giải quyết => setTimeout (myFunc, 1000));
PabloDK

35

setTimeoutkhông phải là một asyncchức năng, vì vậy bạn không thể sử dụng nó với ES7 async-await. Nhưng bạn có thể thực hiện sleepchức năng của mình bằng ES6 Promise :

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

Sau đó, bạn sẽ có thể sử dụng sleepchức năng mới này với ES7 async-await:

var fileList = await sleep(listFiles, nextPageToken)

Xin lưu ý rằng tôi chỉ trả lời câu hỏi của bạn về việc kết hợp ES7 async / await với setTimeout, mặc dù điều đó có thể không giúp giải quyết vấn đề của bạn với việc gửi quá nhiều yêu cầu mỗi giây.


Cập nhật: Các phiên bản node.js hiện đại có triển khai thời gian chờ không đồng bộ buid-in, có thể truy cập thông qua trình trợ giúp ing.promisify :

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

2
Bạn không nên làm điều đó, khi fnném lỗi sẽ không bị bắt.
Bergi

@Bergi Tôi nghĩ nó bong bóng đến new Promisenơi bạn có thể sleep.catch.
Florian Wendelborn

3
@Dodekeract Không, đó là trong một setTimeoutcuộc gọi lại không đồng bộ và cuộc new Promisegọi lại đã được thực hiện từ lâu. Nó sẽ bong bóng đến bối cảnh toàn cầu và được ném như một ngoại lệ chưa được xử lý.
Bergi

> vấn đề với việc gửi quá nhiều yêu cầu mỗi giây. Bạn muốn sử dụng "gỡ lỗi" có lẽ để ngăn chặn những thứ như UI bắn quá nhiều vụ phá hoại.
FlavorScape

5

Nếu bạn muốn sử dụng cùng một loại cú pháp như setTimeoutbạn có thể viết một hàm trợ giúp như thế này:

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

Sau đó, bạn có thể gọi nó như vậy:

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

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

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

doStuffAsync();

Tôi đã thực hiện một ý chính: https://gist.github.com/DaveBitter/f44889a2a52ad16b6a5129c39444bb57


1
một tên hàm như delayRunsẽ có ý nghĩa hơn ở đây, vì nó sẽ trì hoãn việc chạy chức năng gọi lại trong X giây. Không phải là một ví dụ rất chờ đợi, 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

Đoạn mã sau hoạt động trong Chrome và Firefox và có thể các trình duyệt khác.

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

Nhưng trong Internet Explorer, tôi gặp lỗi Cú pháp cho "(resolve **=>** setTimeout..."


0

Tạo cảm hứng từ câu trả lời của Dave

Về cơ bản được chuyển trong một donecuộc gọi lại để gọi khi hoạt động kết thúc.

// 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);
});

Đây là cách tôi sử dụng nó:

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

Đây là phiên bản của tôi với nodejs bây giờ vào năm 2020 trong AWS labdas

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

Đây là một sửa chữa nhanh hơn trong một lót.

Hy vọng điều này sẽ giúp.

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

1
Không hoạt động. Điều này: await setTimeout(()=>{console.log('first')}, 200); console.log ('second')in lần thứ hai rồi đến lần đầu tiên
gregn3

1
@ gregn3 đó là điểm có. Đây là một giải pháp không chặn trong đó mã bên ngoài chức năng có thể tiếp tục thực thi trong khi "hoạt động chặn" được hoàn thành bên ngoài luồng chương trình chính. Mặc dù cú pháp mà bạn và Rommy và Mohamad đã cung cấp không đúng hoàn toàn do yêu cầu chờ đợi để được gõ trong hàm async (có thể là một bổ sung khá gần đây), nhưng tôi cũng đang sử dụng node.js. Đây là giải pháp tinh chỉnh của tôi. var test = async () => { await setTimeout(()=>{console.log('first')}, 1000); console.log ('second') }Tôi đã kéo dài thời gian chờ để thể hiện sự hữu ích của nó.
azariah
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.