上面的写法有一个缺点,就是如果f是同步函数,那么它会在本轮事件循环的末尾执行。

    1. const f = () => console.log('now');
    2. Promise.resolve().then(f);
    3. console.log('next');
    4. // next
    5. // now

    上面代码中,函数f是同步的,但是用 Promise 包装了以后,就变成异步执行了。

    那么有没有一种方法,让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API 呢?回答是可以的,并且还有两种写法。第一种写法是用async函数来写。

    1. const f = () => console.log('now');
    2. (async () => f())();
    3. console.log('next');
    4. // now
    5. // next

    上面代码中,第二行是一个立即执行的匿名函数,会立即执行里面的async函数,因此如果f是同步的,就会得到同步的结果;如果f是异步的,就可以用then指定下一步,就像下面的写法。

    1. .then(...)
    2. .catch(...)

    第二种写法是使用new Promise()

    1. const f = () => console.log('now');
    2. (
    3. () => new Promise(
    4. resolve => resolve(f())
    5. )
    6. )();
    7. console.log('next');
    8. // now

    上面代码也是使用立即执行的匿名函数,执行new Promise()。这种情况下,同步函数也是同步执行的。

    鉴于这是一个很常见的需求,所以现在有一个,提供Promise.try方法替代上面的写法。

    事实上,Promise.try存在已久,Promise 库Bluebird、和when,早就提供了这个方法。

    1. function getUsername(userId) {
    2. return database.users.get({id: userId})
    3. .then(function(user) {
    4. return user.name;
    5. });
    6. }

    上面代码中,database.users.get()返回一个 Promise 对象,如果抛出异步错误,可以用catch方法捕获,就像下面这样写。

    1. database.users.get({id: userId})
    2. .then(...)
    3. .catch(...)

    但是database.users.get()可能还会抛出同步错误(比如数据库连接错误,具体要看实现方法),这时你就不得不用try...catch去捕获。

    上面这样的写法就很笨拙了,这时就可以统一用promise.catch()捕获所有同步和异步的错误。

    1. Promise.try(() => database.users.get({id: userId}))
    2. .catch(...)

    事实上,Promise.try就是模拟try代码块,就像模拟的是catch代码块。