2017年7月7日 星期五

Understand promises

Reference: 參考資料1 參考資料2

Promise是什麼?


  • Promise是一種特別的JavaScript object,它又包含了其他objects。
  • 使用`.then()`來存取promise中的資料
function getFirstUser() {
    return getUsers().then(function(users) {
        return users[0].name;
    });
}


  • 使用`.catch()`來catch error
function getFirstUser() {
    return getUsers().then(function(users) {
        return users[0].name;
    }).catch(function(err) {
        return {
          name: 'default user'
        };
    });
}


  • Promise是asynchronous的,不論現在就已經有資料或是將來才會有資料回傳。
  • 在上面的code中getUsers()回傳一個Promise。使用ES2016,任何Promise都可以await。await等同於對Promise呼叫`.then( )`而且不需要callback。程式可以改寫如下:
async function getFirstUser() {
    let users = await getUsers();
    return users[0].name;
}


  • await會讓method暫停執行,直到Promise的值是available。
  • 而catch error的方式也可改用synchronous型式的try/catch:
async function getFirstUser() {
    try {
        let users = await getUsers();
        return users[0].name;
    } catch (err) {
        return {
            name: 'default user'
        };
    }
}

Awaiting multiple values


  • 一般的使用下,一次只能await一個東西,下面這段code會依序執行await取得foo跟bar
let foo = await getFoo();
let bar = await getBar();


  • 若我們想讓foo跟bar同時被設置,則可用下列code:
let [foo, bar] = await Promise.all([getFoo(), getBar()]);


  • Promise.all( )是把一個array的Promises組合成單一的Promise,且array中的所有child promise都resolve後才算是真的resolve。
  • Promise.all( )並沒有dispatch你帶進去的Promise,當[getFoo(), getBar()]執行時就已經在做dispatch的動作了。
  • 還有另外一個有趣的方法可以讓async/await可以parallel執行如下:
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;


  • 這段程式碼需要你對Promise有所理解:
    1. 首先dispatch `getFoo`及`getBar`並將其回傳的Promise存在fooPromise及barPromise
    2. 這些動作正在進行中,沒有stop或delay發生
    3. 接著去await各個Promise
    4. 由於在await前就把所有async action都dispatch了,所以它們會同時執行
    5. foo跟bar

Handle callbacks


  • 當你想在Promise的值available時呼叫callback,可用下列方法:
function getFirstUser(callback) {
    return getUsers().then(function(user) {
        return callback(null, users[0].name);
    }).catch(function(err) {
        return callback(err);
    });
}


  • 若在async function裡想要呼叫callback,則須把callback轉成會回傳Promise的function:
function callbackToPromise(method, ...args) {
    return new Promise(function(resolve, reject) {
        return method(...args, function(err, result) {
            return err ? reject(err) : resolve(result);
        });
    });
}
async function getFirstUser() {
    let users = await callbackToPromise(getUsers);
    return users[0].name;
}



  • 記得只要用到sync function就要去catch error做處理


Using side effects instead of returning


  • 在Promise的then( )或catch( )能做三件事
  1. 回傳另一個Promise
    • 這是一個常見的pattern用來組合Promise
getUserByName('nolan').then(function (user) {
  return getUserAccountById(user.id);
}).then(function (userAccount) {
  // I got a user account!
});


    •    注意:那個return是重要的!若沒有加上return,則第二個Promise的function會收到undefined而非userAccount。
  1. 回傳一個synchronous的值

    • 第二個Promise的 function並不在意參數是 sync或是async地取得
getUserByName('nolan').then(function (user) {
  if (inMemoryCache[user.id]) {
    return inMemoryCache[user.id];    // returning a synchronous value!
  }
  return getUserAccountById(user.id); // returning a promise!
}).then(function (userAccount) {
  // I got a user account!
});


  1. Throw synchronous error 
getUserByName('nolan').then(function (user) {
  if (user.isLoggedOut()) {
    throw new Error('user logged out!'); // throwing a synchronous error!
  }
  if (inMemoryCache[user.id]) {
    return inMemoryCache[user.id];       // returning a synchronous value!
  }
  return getUserAccountById(user.id);    // returning a promise!
}).then(function (userAccount) {
  // I got a user account!
}).catch(function (err) {
  // Boo, I got an error!
});

Promises fall through


  • 你以為下列的code會印出bar但實際上會印出boo,這是因為若then( )裡面帶的不是function,則會被視為then(null),前一個Promise的結果會往下傳。
Promise.resolve('foo').then(Promise.resolve('bar')).then(function (result) {
  console.log(result);
});





沒有留言:

張貼留言