假设我有一组promise正在发出网络请求,其中一个将失败:
// http://does-not-exist will throw a TypeError
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]
Promise.all(arr)
.then(res => console.log('success', res))
.catch(err => console.log('error', err)) // This is executed
假设我想要等到所有这些都完成,不管是否有一个失败了。可能有一个资源的网络错误,我可以没有,但如果我能得到,我想在继续之前。我想优雅地处理网络故障。
因为承诺。所有这些都没有留下任何空间,在不使用承诺库的情况下,处理这个问题的推荐模式是什么?
我也遇到过同样的问题,我用下面的方法解决了这个问题:
const fetch = (url) => {
return node-fetch(url)
.then(result => result.json())
.catch((e) => {
return new Promise((resolve) => setTimeout(() => resolve(fetch(url)), timeout));
});
};
tasks = [fetch(url1), fetch(url2) ....];
Promise.all(tasks).then(......)
那样的话,答应我。所有人都在等待每一个承诺都将进入解决或拒绝的状态。
有了这个解决方案,我们以一种非阻塞的方式“停止捕获执行”。事实上,我们并没有停止任何事情,我们只是返回处于挂起状态的Promise,当它在超时后被解决时,它会返回另一个Promise。
从ES5开始,我一直在使用以下代码。
Promise.wait = function(promiseQueue){
if( !Array.isArray(promiseQueue) ){
return Promise.reject('Given parameter is not an array!');
}
if( promiseQueue.length === 0 ){
return Promise.resolve([]);
}
return new Promise((resolve, reject) =>{
let _pQueue=[], _rQueue=[], _readyCount=false;
promiseQueue.forEach((_promise, idx) =>{
// Create a status info object
_rQueue.push({rejected:false, seq:idx, result:null});
_pQueue.push(Promise.resolve(_promise));
});
_pQueue.forEach((_promise, idx)=>{
let item = _rQueue[idx];
_promise.then(
(result)=>{
item.resolved = true;
item.result = result;
},
(error)=>{
item.resolved = false;
item.result = error;
}
).then(()=>{
_readyCount++;
if ( _rQueue.length === _readyCount ) {
let result = true;
_rQueue.forEach((item)=>{result=result&&item.resolved;});
(result?resolve:reject)(_rQueue);
}
});
});
});
};
用法签名就像Promise.all。主要的区别在于《承诺》。等待会等待所有的承诺来完成自己的工作。
我只是想要一个完全复制ES2020行为的填充,因为我被锁定在比12.9更早的节点版本(当Promise。allsettle出现了),不幸的是。所以不管怎样,这是我的版本:
const settle = (promise) => (promise instanceof Promise) ?
promise.then(val => ({ value: val, status: "fulfilled" }),
err => ({ reason: err, status: "rejected" })) :
{ value: promise, status: 'fulfilled' };
const allSettled = async (parr) => Promise.all(parr.map(settle));
它处理承诺值和非承诺值的混合数组,就像ES版本一样。它返回与原生版本相同的{status, value/reason}对象数组。
与其拒绝,不如用一个对象来解决。
当你实现承诺时,你可以这样做
Const promise = arg => {
返回新的承诺((resolve, reject) => {
setTimeout(() => {
尝试{
If (arg != 2)
返回解析({success: true, data: arg});
其他的
抛出新的错误(arg)
}捕捉(e) {
返回解析({success: false, error: e, data: arg})
}
}, 1000);
})
}
Promise.all([1、2、3、4、5)。Map (e => promise(e)))。然后(d => console.log(d))
你可以通过同步执行器nsynjs按顺序执行你的逻辑。它将在每个承诺上暂停,等待解析/拒绝,并将解析的结果分配给data属性,或者抛出异常(用于处理您将需要try/catch块)。这里有一个例子:
function synchronousCode() {
function myFetch(url) {
try {
return window.fetch(url).data;
}
catch (e) {
return {status: 'failed:'+e};
};
};
var arr=[
myFetch("https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"),
myFetch("https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/NONEXISTANT.js"),
myFetch("https://ajax.NONEXISTANT123.com/ajax/libs/jquery/2.0.0/NONEXISTANT.js")
];
console.log('array is ready:',arr[0].status,arr[1].status,arr[2].status);
};
nsynjs.run(synchronousCode,{},function(){
console.log('done');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>
Benjamin Gruenbaum的回答当然很好。但我也能看出内森哈根的观点在抽象层面上显得模糊。拥有像e和v这样的短对象属性也没有帮助,但当然这是可以改变的。
在Javascript中有一个标准的Error对象,称为Error。理想情况下,您总是抛出该实例/后代。这样做的好处是您可以使用instanceof Error,并且您知道某些东西是错误的。
利用这个想法,下面是我对这个问题的看法。
基本上捕捉错误,如果错误不是error类型,则将错误包装在error对象中。结果数组将具有已解析的值或可以检查的Error对象。
catch内部的instanceof,是为了防止你使用一些外部库可能会reject("error"),而不是reject(new error ("error"))。
当然,如果您解决了一个错误,您也可以有承诺,但在这种情况下,无论如何都应该将其视为错误,就像最后一个示例所示的那样。
这样做的另一个好处是,数组析构保持简单。
const [value1, value2] = PromiseAllCatch(promises);
if (!(value1 instanceof Error)) console.log(value1);
而不是
const [{v: value1, e: error1}, {v: value2, e: error2}] = Promise.all(reflect..
if (!error1) { console.log(value1); }
你可能会说!error1检查比instanceof简单,但你也必须销毁v和e。
function PromiseAllCatch(promises) {
return Promise.all(promises.map(async m => {
try {
return await m;
} catch(e) {
if (e instanceof Error) return e;
return new Error(e);
}
}));
}
async function test() {
const ret = await PromiseAllCatch([
(async () => "this is fine")(),
(async () => {throw new Error("oops")})(),
(async () => "this is ok")(),
(async () => {throw "Still an error";})(),
(async () => new Error("resolved Error"))(),
]);
console.log(ret);
console.log(ret.map(r =>
r instanceof Error ? "error" : "ok"
).join(" : "));
}
test();