如何在async / await语法中拒绝?

时间:2017-02-25 08:20:24

标签: javascript asynchronous typescript es6-promise ecmascript-2017

如何拒绝async / await函数返回的承诺?

e.g。原本

foo(id: string): Promise<A> {
  return new Promise((resolve, reject) => {
    someAsyncPromise().then((value)=>resolve(200)).catch((err)=>reject(400))
  });
}

转换为async / await

async foo(id: string): Promise<A> {
  try{
    await someAsyncPromise();
    return 200;
  } catch(error) {//here goes if someAsyncPromise() rejected}
    return 400; //this will result in a resolved promise.
  });
}

那么,在这种情况下我怎么能正确拒绝这个承诺?

7 个答案:

答案 0 :(得分:221)

您最好的选择是throw Error包装该值,这会导致被拒绝的承诺,Error包裹该值:

} catch (error) {
    throw new Error(400);
}

您也可以只throw该值,但是没有堆栈跟踪信息:

} catch (error) {
    throw 400;
}

或者,返回一个被拒绝的承诺,其中包含Error值:

} catch (error) {
    return Promise.reject(new Error(400));
}

(或者只是return Promise.reject(400);,但是再次,那里没有上下文信息。)

(在您的情况下,当您使用TypeScriptfoo的后续值为Promise<A>时,您将使用return Promise.reject<A>(400 /*or error*/);

async / await情况下,最后一个可能是语义不匹配,但确实有效。

如果你抛出一个Error,那么使用foo语法await的结果就可以很好地解决这个问题:

try {
    await foo();
} catch (error) {
    // Here, `error` would be an `Error` (with stack trace, etc.).
    // Whereas if you used `throw 400`, it would just be `400`.
}

答案 1 :(得分:109)

It should probably also be mentioned that you can simply chain a catch() function after the call of your async operation because under the hood still a promise is returned.

await foo().catch(error => console.log(error));

This way you can avoid the try/catch syntax if you do not like it.

答案 2 :(得分:9)

您可以创建包装函数,其中包含承诺,如果没有错误则返回包含数据的数组,如果出现错误则返回错误。

function safePromise(promise) {
  return promise.then(data => [ data ]).catch(error => [ null, error ]);
}

ES7 异步功能中使用它:

async function checkItem() {
  const [ item, error ] = await safePromise(getItem(id));
  if (error) { return null; } // handle error and return
  return item; // no error so safe to use item
}

答案 3 :(得分:4)

编写异步函数的更好方法是从一开始就返回一个挂起的Promise,然后在promise的回调中处理拒绝和解析,而不是仅仅在错误时吐出被拒绝的promise。例如:

async foo(id: string): Promise<A> {
    return new Promise(function(resolve, reject) {
        // execute some code here
        if (success) { // let's say this is a boolean value from line above
            return resolve(success);
        } else {
            return reject(error); // this can be anything, preferably an Error object to catch the stacktrace from this function
        }
    });
}

然后,您只需在返回的承诺上链接方法:

async function bar () {
    try {
        var result = await foo("someID")
        // use the result here
    } catch (error) {
        // handle error here
    }
}

bar()

来源 - 本教程:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

答案 4 :(得分:3)

这不是@ T.J的答案。克劳德的一个。只是回复评论的评论&#34;实际上,如果异常将被转换为拒绝,我不确定如果它是错误我是否真的感到困扰。我抛出错误的原因可能不适用。&#34;

如果您的代码使用的是async / await,那么拒绝使用Error代替400仍然是一个好习惯:

try {
  await foo('a');
}
catch (e) {
  // you would still want `e` to be an `Error` instead of `400`
}

答案 5 :(得分:2)

我知道这是一个老问题,但是我偶然发现了这个线程,并且错误和拒绝之间似乎存在混淆,这会干扰(至少在很多情况下)经常重复使用的不使用的建议处理预期情况的异常处理。举例说明:如果异步方法尝试对用户进行身份验证而身份验证失败,则表示拒绝(两种预期情况之一),而不是错误(例如,如果身份验证API不可用)。

为了确保我不仅仅是在分裂头发,我使用以下代码对三种不同的方法进行了性能测试:

const iterations = 100000;

function getSwitch() {
  return Math.round(Math.random()) === 1;
}

function doSomething(value) {
  return 'something done to ' + value.toString();
}

let processWithThrow = function () {
  if (getSwitch()) {
    throw new Error('foo');
  }
};

let processWithReturn = function () {
  if (getSwitch()) {
    return new Error('bar');
  } else {
    return {}
  }
};

let processWithCustomObject = function () {
  if (getSwitch()) {
    return {type: 'rejection', message: 'quux'};
  } else {
    return {type: 'usable response', value: 'fnord'};
  }
};

function testTryCatch(limit) {
  for (let i = 0; i < limit; i++) {
    try {
      processWithThrow();
    } catch (e) {
      const dummyValue = doSomething(e);
    }
  }
}

function testReturnError(limit) {
  for (let i = 0; i < limit; i++) {
    const returnValue = processWithReturn();
    if (returnValue instanceof Error) {
      const dummyValue = doSomething(returnValue);
    }
  }
}

function testCustomObject(limit) {
  for (let i = 0; i < limit; i++) {
    const returnValue = processWithCustomObject();
    if (returnValue.type === 'rejection') {
      const dummyValue = doSomething(returnValue);
    }
  }
}

let start, end;
start = new Date();
testTryCatch(iterations);
end = new Date();
const interval_1 = end - start;
start = new Date();
testReturnError(iterations);
end = new Date();
const interval_2 = end - start;
start = new Date();
testCustomObject(iterations);
end = new Date();
const interval_3 = end - start;

console.log(`with try/catch: ${interval_1}ms; with returned Error: ${interval_2}ms; with custom object: ${interval_3}ms`);

由于我对Java语言解释器的不确定性,其中包含了一些东西(我只喜欢一次钻一个兔子洞);例如,我加入了doSomething函数并将其返回值分配给dummyValue以确保条件块不会被优化。

我的结果是:

with try/catch: 507ms; with returned Error: 260ms; with custom object: 5ms

我知道在很多情况下寻找小小的优化是不值得的,但是在大型系统中,这些事情可能会产生很大的累加差异,这是一个相当鲜明的比较。

所以……虽然我认为在您期望必须处理异步函数中不可预测的错误的情况下,可接受的答案是合理的,但在拒绝仅意味着“您将不得不使用Plan的情况下” B(或C或D…)”,我认为我的首选是拒绝使用自定义响应对象。

答案 6 :(得分:0)

我建议采用一种新颖的方法正确地处理拒绝,而不必使用多个try-catch块。

import to from './to';

async foo(id: string): Promise<A> {
    let err, result;
    [err, result] = await to(someAsyncPromise()); // notice the to() here
    if (err) {
        return 400;
    }
    return 200;
}

to.ts 函数的导入位置:

export default function to(promise: Promise<any>): Promise<any> {
    return promise.then(data => {
        return [null, data];
    }).catch(err => [err]);
}

以下link中的信用归Dima Grossman所有。