Promise 可以按顺序加载多个 url 吗? [英] Can Promise load multi urls in order?

查看:20
本文介绍了Promise 可以按顺序加载多个 url 吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

promise then return a specified promise 给出以下代码片段:

function get(url) {
  return new Promise(function(resolve, reject) {
    var req = new XMLHttpRequest();
    req.open('GET', url);

    req.onload = function() {
      if (req.status == 200) {
        resolve(req.response);
      }
      else {
        reject(Error(req.statusText));
      }
    };
    req.onerror = function() {
      reject(Error("Network Error"));
    };
    req.send();
  });
}

这个函数可以如下使用:

This function can be used as following:

get('story.json').then(function(response) {
  console.log("Success!", response);
}, function(error) {
  console.error("Failed!", error);
});

我想用promise来加载多个url,比如

I want to use promise to load multi urls, such as

get('a.json').get('b.json')
// or get('a.json').then().get('b.json')

我已经通过其他方式实现了它.但据我了解,Promise 做不到这个工作.真的吗?

I have implemented it in other ways. But as I understand it, Promise can not do this work. Really ?

事实上,我实现了一个类似的库来帮助在浏览器中执行动态脚本:

In fact, I implemented a similar lib that help execute dynamic scripts in browsers:

Loader = (function() {

  var load_cursor = 0;
  var load_queue;

  var loadFinished = function() {
    load_cursor ++;
    if (load_cursor < load_queue.length) {
      loadScript();
    }
  }

  function loadError (oError) {
    console.error("The script " + oError.target.src + " is not accessible.");
  }


  var loadScript = function() {
    var url = load_queue[load_cursor];
    var script = document.createElement('script');
    script.type = "text/javascript";

    if (script.readyState){  //IE
        script.onreadystatechange = function(){
            if (script.readyState == "loaded" ||
                    script.readyState == "complete"){
                script.onreadystatechange = null;
                loadFinished();
            }
        };
    } else {  //Others
        script.onload = function(){
            loadFinished();
        };
    }

    script.onerror = loadError;

    script.src = url+'?'+'time='+Date.parse(new Date());
    document.body.appendChild(script);
  };

  var loadMultiScript = function(url_array) {
    load_cursor = 0;
    load_queue = url_array;
    loadScript();
  }

  return {
    load: loadMultiScript,
  };

})();  // end Loader


// load...

Loader.load(['http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js', './my.js']);

推荐答案

使用普通 Promise 语法

链式承诺

如果您希望它们按顺序排列,您可以链接您的承诺:

You can chain your promises if you want them sequential:

get('a.json').then(function(a) {
    return get('b.json');
}).then(function(b){
    return get('c.json');
}).then(function(c) {
    // all done here
}), function(err) {
    // error here
});

或者在 ES7 中,你可以像这样使用 async/await:

Or in ES7, you can use async/await like this:

async function someFunction() {
    let a = await get('a.json');
    let b = await get('b.json');
    // do something with a and b here
    return something;
}

someFunction().then(result => {
    // all done here
}).catch(err => {
    // error here
});

并行运行承诺

如果你想让它们并行加载,你可以使用 Promise.all():

If you want them loaded in parallel, you can use Promise.all():

Promise.all([get('a.json'), get('b.json'), get('c.json')]).then(function(results) {
    // all done with results here
}, function(err) {
    // error here
});

使用 .reduce() 的序列

或者,如果您使用相同的代码来处理每个结果,您可以使用 reduce() 设计模式顺序加载它们:

Or, if you use the same code to process each result, you can load them sequentially using the reduce() design pattern:

['a.json', 'b.json', 'c.json'].reduce(function(p, item) {
    return p.then(function(){
        // process item here
    });
}, Promise.resolve()).then(function(result) {
    // all done here
}, function(err) {
    // error here
});

使用 Bluebird 的 .map() 的序列

或者,如果使用 Bluebird promise 库,它具有 Promise.map(),这对于数组上的并行操作非常有用

Or, if using the Bluebird promise library, it has Promise.map() which is very useful for parallel operations on an array

Promise.map(['a.json', 'b.json', 'c.json'], function(item) {
    // do some operation on item and return data or a promise
    return something;
}).then(function(results) {
    // all done
}, function(err) {
    // error here
});


使 get(x).get(y).get(z) 工作

扩展承诺

我对使用 Promise 的 get(x).get(y).get(z) 问题很感兴趣.我在这个工作片段中想出了一种方法来做到这一点:

I was intrigued by the get(x).get(y).get(z) question using promises. I conjured up a way to do that in this working snippet:

function get(url) {
    function pget(u) {
        var p = this.then(function (result) {
            log(result);
            return get(u);
        });
        p.get = pget;
        return p;
    }
    var p = new Promise(function (resolve, reject) {
        setTimeout(function() {
            resolve(url);
        }, 500);
    });
    p.get = pget;
    return p;
}

get('a.json').get('b.json').get('c.json').then(function(result) {
    log(result);
    log("done");
}, function(err) {
    log("err");
});

function log(x) {
    var div = document.createElement("div");
    div.innerHTML = x;
    document.body.appendChild(div);
}

这有点像黑客.我认为标准机构正在研究一种更正式的方式来扩展这样的承诺,这种承诺将在比这更多的情况下起作用.但是,我在 Chrome、Firefox 和 IE 中使用 Bluebird 尝试了它,这种特殊用法适用于所有三个.这种方法的挑战在于,每个 .then() 都会创建一个新的 promise,并且默认情况下,它不会包含您的 .get() 方法.由于它的使用方式,我们在这里侥幸成功,但您必须小心使用它.

This is a bit of a hack. I think the standards body is working on a more official way to extend a promise like this that will work in more cases than this will. But, I tried it in Chrome, Firefox and IE with Bluebird and this particular usage works in all three. The challenge with this method is that every .then() creates a new promise and, by default, it will not have your .get() method on it. We get away with it here because of the way it is used, but you would have to be careful exactly how you use this.

链接我们自己的对象

这里有一种不同的方式来执行 get(x).get(y).get(z) 语法.它在末尾需要一个空白的 .get() 来告诉你它想要停止链并访问最终的承诺.这种方案的优点是它不会以任何方式干扰 promise 对象.

Here's a bit different way to do the get(x).get(y).get(z) syntax. It requires a blank .get() at the end to tell you it want to stop the chain and get access to the final promise. The advantage of this scheme is that it doesn't mess with the promise object in any way.

function delay(url) {
    return new Promise(function (resolve, reject) {
        setTimeout(function() {
            log(url);
            resolve(url);
        }, 500);
    });
}

function get(url) {
    
    function pget(u) {
        if (!u) {
            return this.promise;
        } else {
            return {promise: this.promise.then(function() {
                return delay(u);
            }), get: pget}
        }
    }
    
    return {promise: delay(url), get: pget};
}

// note the empty .get() right before the .then()
// the empty .get() at the end is required in this scheme
get('a.json').get('b.json').get('c.json').get().then(function(result) {
    log("done");
}, function(err) {
    log("err");
});


function log(x) {
    var div = document.createElement("div");
    div.innerHTML = x;
    document.body.appendChild(div);
}

这篇关于Promise 可以按顺序加载多个 url 吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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