JavaScript ES6承诺循环 [英] JavaScript ES6 promise for loop

查看:86
本文介绍了JavaScript ES6承诺循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

($ i $ 0; i< 10; i ++){
const promise = new Promise((resolve,reject)=> {
const timeout = Math.random()* 1000;
setTimeout(()=> {
console.log(i);
},timeout);
});

// TODO:把这个承诺链接到前一个(也许没有它运行?)
}

以上将给出以下随机输出:

  6 
9
4
8
5
1
7
2
3
0

任务很简单:确保每个承诺仅在另一个承诺之后运行( .then())。



出于某种原因,我找不到办法。



I尝试了生成器函数( yield ),尝试了返回promise的简单函数,但在一天结束时它总是归结为同样的问题:循环是同步



使用 async 我只是使用 async.series()



你如何解决它?

解决方案

正如您在问题中所暗示的,您的代码会创建所有promi同步。相反,它们只应在前一个结算时创建。



其次,每个承诺需要通过调用 resolve来解决(或拒绝)。这应该在计时器到期时完成。这将触发您对该承诺的任何然后回调。而这样的然后回调(或等待)是实现链的必要条件。



使用这些成分,有几种方法可以执行此异步链接:


  1. 使用 for 循环,以立即解决的承诺开始


  2. 使用数组#reduce 以立即解决的承诺开头


  3. 使用在解析时自行调用的函数


  4. 使用ECMAScript2017的 async / 等待语法


请参阅以下每个选项的摘要和评论。



1。



可以使用循环,但你必须确保它不同步执行 new Promise 。相反,你创建一个初始立即解决的承诺,然后链接新的承诺,因为前面的承诺解决:



  for(let i = 0,p = Promise.resolve(); i< 10; i ++){p = p.then(_ => new Promise(resolve => setTimeout(function() {console.log(i); resolve();},Math.random()* 1000)));}  



2。使用 reduce



这只是对以前策略更具功能性的方法。您创建一个与您要执行的链长度相同的数组,并从一个立即解决的承诺开始:



  [... Array(10)]。reduce((p,_,i)=> p.then(_ => new Promise(resolve => setTimeout(function) (){console.log(i); resolve();},Math.random()* 1000))),Promise.resolve());  



当你实际上拥有一个数据以便在promises中使用时,这可能更有用。



3。函数在解析后自行调用



这里我们创建一个函数并立即调用它。它同步创造了第一个承诺。当它结算时,再次调用该函数:



 (函数循环(i) ){if(i< 10)new Promise((resolve,reject)=> {setTimeout(()=> {console.log(i); resolve();},Math.random()* 1000) ;})。then(loop.bind(null,i + 1));})(0);  



这将创建一个名为 loop 的函数,在代码的最后,您可以看到它会立即被参数0调用。这是计数器,以及 i 参数。如果该计数器仍然低于10,该函数将创建一个新的承诺,否则链接将停止。



的调用resolve()将触发然后回调,这将再次调用该函数。 loop.bind(null,i + 1)只是另一种说法 _ => loop(i + 1)



4。使用 async / 等待



现代JS引擎支持此语法



 (async function loop(){for(let i = 0; i< 10; i ++){等待新的Promise(resolve => setTimeout(resolve,Math.random()* 1000)); console.log(i);}})();  



它可能看起来很奇怪,因为似乎就像新的Promise()调用是同步执行的,但实际上 async 函数在执行第一个 await时返回 。每当等待的承诺解析时,函数的运行上下文将被恢复,并在 await 之后继续,直到它遇到下一个,然后一直持续到循环结束。 / p>

由于基于超时返回承诺可能是常见的事情,您可以创建一个单独的函数来生成这样的承诺。这称为 promisifying 一个函数,在本例中为 setTimeout 。它可以提高代码的可读性:



  const delay = ms => ; new Promise(resolve => setTimeout(resolve,ms));(async function loop(){for(let i = 0; i< 10; i ++){await delay(Math.random()* 1000); console .log(i);}})();  


for (let i = 0; i < 10; i++) {
    const promise = new Promise((resolve, reject) => {
        const timeout = Math.random() * 1000;
        setTimeout(() => {
            console.log(i);
        }, timeout);
    });

    // TODO: Chain this promise to the previous one (maybe without having it running?)
}

The above will give the following random output:

6
9
4
8
5
1
7
2
3
0

The task is simple: Make sure each promise runs only after the other one (.then()).

For some reason, I couldn't find a way to do it.

I tried generator functions (yield), tried simple functions that return a promise, but at the end of the day it always comes down to the same problem: The loop is synchronous.

With async I'd simply use async.series().

How do you solve it?

解决方案

As you already hinted in your question, your code creates all promises synchronously. Instead they should only be created at the time the preceding one resolves.

Secondly, each promise needs to be resolved with a call to resolve (or reject). This should be done when the timer expires. That will trigger any then callback you would have on that promise. And such a then callback (or await) is a necessity in order to implement the chain.

With those ingredients, there are several ways to perform this asynchronous chaining:

  1. With a for loop that starts with an immediately resolving promise

  2. With Array#reduce that starts with an immediately resolving promise

  3. With a function that calls itself upon resolution

  4. With ECMAScript2017's async / await syntax

See a snippet and comments for each of these options below.

1. With for

You can use a for loop, but you must make sure it doesn't execute new Promise synchronously. Instead you create an initial immediately resolving promise, and then chain new promises as the previous ones resolve:

for (let i = 0, p = Promise.resolve(); i < 10; i++) {
    p = p.then(_ => new Promise(resolve =>
        setTimeout(function () {
            console.log(i);
            resolve();
        }, Math.random() * 1000)
    ));
}

2. With reduce

This is just a more functional approach to the previous strategy. You create an array with the same length as the chain you want to execute, and start out with an immediately resolving promise:

[...Array(10)].reduce( (p, _, i) => 
    p.then(_ => new Promise(resolve =>
        setTimeout(function () {
            console.log(i);
            resolve();
        }, Math.random() * 1000)
    ))
, Promise.resolve() );

This is probably more useful when you actually have an array with data to be used in the promises.

3. Function calling itself upon resolution

Here we create a function and call it immediately. It creates the first promise synchronously. When it resolves, the function is called again:

(function loop(i) {
    if (i < 10) new Promise((resolve, reject) => {
        setTimeout( () => {
            console.log(i);
            resolve();
        }, Math.random() * 1000);
    }).then(loop.bind(null, i+1));
})(0);

This creates a function named loop, and at the very end of the code you can see it gets called immediately with argument 0. This is the counter, and the i argument. The function will create a new promise if that counter is still below 10, otherwise the chaining stops.

The call to resolve() will trigger the then callback which will call the function again. loop.bind(null, i+1) is just a different way of saying _ => loop(i+1).

4. With async/await

Modern JS engines support this syntax:

(async function loop() {
    for (let i = 0; i < 10; i++) {
        await new Promise(resolve => setTimeout(resolve, Math.random() * 1000));
        console.log(i);
    }
})();

It may look strange, as it seems like the new Promise() calls are executed synchronously, but in reality the async function returns when it executes the first await. Every time an awaited promise resolves, the function's running context is restored, and proceeds after the await, until it encounters the next one, and so it continues until the loop finishes.

As it may be a common thing to return a promise based on a timeout, you could create a separate function for generating such a promise. This is called promisifying a function, in this case setTimeout. It may improve the readability of the code:

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

(async function loop() {
    for (let i = 0; i < 10; i++) {
        await delay(Math.random() * 1000);
        console.log(i);
    }
})();

这篇关于JavaScript ES6承诺循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆