【问题标题】:Can Promise load multi urls in order?Promise 可以按顺序加载多个 url 吗?
【发布时间】:2015-11-24 06:08:32
【问题描述】:

promise then return a specified promise 给出以下代码 sn-p:

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

这个函数可以如下使用:

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

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

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

我已经以其他方式实现了它。但据我了解,Promise 无法完成这项工作。真的吗?

添加

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

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']);

【问题讨论】:

  • 你已经接近了。你需要学习如何正确使用then,你会被设置
  • .get 不是 promise 的方法,它只是一个普通的函数。听起来你想要Promise.all([get('a'), get('b')]).then(function([aresult, bresult]) { … });
  • 你能告诉我们你的“其他方式”吗?很难确定您真正想要什么,因为“加载多个 url”是微不足道的,而且您错误的承诺示例也无济于事。
  • 不应该将loadMultiScript 附加到load_queue,将load_cursor 重置为0 并开始第一个 loadScript()(然后让@987654335 @做剩下的),而不是在一个循环中多次调用loadScript()?这将并行加载每个,而不是按顺序。
  • 我想你会想阅读你链接的文章中的html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues章节

标签: javascript promise


【解决方案1】:

使用普通的 Promise 语法

链承诺

如果你希望它们是连续的,你可以链接你的承诺:

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

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

如果你想让它们并行加载,你可以使用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() 设计模式依次加载它们:

['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() 这对于数组上的并行操作非常有用

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)工作

履行承诺

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

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() 方法。由于它的使用方式,我们在这里侥幸逃脱,但你必须小心你如何使用它。

链接我们自己的对象

这里有一点不同的方式来执行get(x).get(y).get(z) 语法。它最后需要一个空白的.get() 来告诉你它想要停止链并获得最终承诺。这种方案的优点是它不会以任何方式与 Promise 对象混淆。

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

【讨论】:

  • 简洁的答案。非常感谢
  • @defau1t - 我又添加了两个选项。
  • @jjfriend00 不幸的是我不能双重投票.. 不过还是谢谢
  • @defau1t - 我添加了一种方法来做get(x).get(y).get(z)
【解决方案2】:

你可以加入promises,在这种情况下你必须从成功回调中返回下一个promise

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

【讨论】:

  • 会导致“回调地狱”。
  • 这不是回调地狱,回调地狱是在成功回调中做每一件事
猜你喜欢
  • 1970-01-01
  • 2012-01-19
  • 2015-08-19
  • 1970-01-01
  • 2019-06-11
  • 2022-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多