How to reject in async/await syntax?

后端 未结 7 1784
无人及你
无人及你 2020-11-28 17:56

How can I reject a promise that returned by an async/await function?

e.g. Originally:

         


        
7条回答
  •  没有蜡笔的小新
    2020-11-28 18:15

    I know this is an old question, but I just stumbled across the thread and there seems to be a conflation here between errors and rejection that runs afoul (in many cases, at least) of the oft-repeated advice not to use exception handling to deal with anticipated cases. To illustrate: if an async method is trying to authenticate a user and the authentication fails, that's a rejection (one of two anticipated cases) and not an error (e.g., if the authentication API was unavailable.)

    To make sure I wasn't just splitting hairs, I ran a performance test of three different approaches to that, using this code:

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

    Some of the stuff that's in there is included because of my uncertainty regarding the Javascript interpreter (I only like to go down one rabbit hole at a time); for instance, I included the doSomething function and assigned its return to dummyValue to ensure that the conditional blocks wouldn't get optimized out.

    My results were:

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

    I know that there are plenty of cases where it's not worth the trouble to hunt down small optimizations, but in larger-scale systems these things can make a big cumulative difference, and that's a pretty stark comparison.

    SO… while I think the accepted answer's approach is sound in cases where you're expecting to have to handle unpredictable errors within an async function, in cases where a rejection simply means "you're going to have to go with Plan B (or C, or D…)" I think my preference would be to reject using a custom response object.

提交回复
热议问题