JavaScript Promise API

儘管同步代碼易於追蹤和調試,但異步代碼廣泛在性能和靈活性上更具優點。Why "hold up the show" when you can trigger numerous requests at once and then handle them when each is ready?(這句要怎麼翻??)promise和許多基於promise的新的API已經成爲JavaScript世界重要的一部分。讓咱們來看一下promise的API如何來使用。javascript

Promises in the Wild

XMLHttpRequest API是異步的但它並無用Promises API,如今有一些native APIs正在使用promises:html

  • Battery API(譯者注:這篇文章我也有翻譯前端

  • fetch API(XHR的取代者)html5

  • ServiceWorker API(關於這個API的文章正在路上)java

promises會變得很流行,因此前端開發者們都應該去學習它。毫無疑問,Node.js是promises另外一個重要的平臺(顯然,promises是一個核心語法特性)。es6

測試 promises 比你想得容易得多,由於 setTimeout 能夠模擬你的異步「任務」json

Basic Promise Usage

構造函數 new Promise() 應該只用於老式的異步任務,好比 setTimeout 或者 XMLHttpRequest。咱們使用 new 關鍵字和promise提供的 resolvereject 回調函數來建立新的 promise:api

var p = new Promise(function(resolve, reject) {
    
    // Do an async task async task and then...

    if(/* good condition */) {
        resolve('Success!');
    }
    else {
        reject('Failure!');
    }
});

p.then(function() { 
    /* do something with the result */
}).catch(function() {
    /* error :( */
})

根據異步任務的返回結果,開發者能夠在回調函數體的內部手動調用 resolve 或者 reject。把 XMLHttpRequest 轉化爲基於promise的任務就是一個實際的例子:數組

// From Jake Archibald's Promises and Back:
// http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest

function get(url) {
  // Return a new promise.
  return new Promise(function(resolve, reject) {
    // Do the usual XHR stuff
    var req = new XMLHttpRequest();
    req.open('GET', url);

    req.onload = function() {
      // This is called even on 404 etc
      // so check the status
      if (req.status == 200) {
        // Resolve the promise with the response text
        resolve(req.response);
      }
      else {
        // Otherwise reject with the status text
        // which will hopefully be a meaningful error
        reject(Error(req.statusText));
      }
    };

    // Handle network errors
    req.onerror = function() {
      reject(Error("Network Error"));
    };

    // Make the request
    req.send();
  });
}

// Use it!
get('story.json').then(function(response) {
  console.log("Success!", response);
}, function(error) {
  console.error("Failed!", error);
});

Sometimes you don't need to complete an async tasks within the promise -- if it's possible that an async action will be taken, however, returning a promise will be best so that you can always count on a promise coming out of a given function.(譯者注:求你們幫忙翻譯。。)這樣你就能夠簡單地調用Promise.resolve() 或者 Promise.reject() 而不須要使用 new 關鍵字。舉例說明:promise

var userCache = {};

function getUserDetail(username) {
  // In both cases, cached or not, a promise will be returned

  if (userCache[username]) {
      // Return a promise without the "new" keyword
    return Promise.resolve(userCache[username]);
  }

  // Use the fetch API to get the information
  // fetch returns a promise
  return fetch('users/' + username + '.json')
    .then(function(result) {
      userCache[username] = result;
      return result;
    })
    .catch(function() {
      throw new Error('Could not find user: ' + username);
    });
}

上面的函數老是返回一個promise對象,你老是能夠對這個返回值使用then 或者 catch方法。

then

全部的promise實例都有 then 方法,來對這個promise實例作進一步處理。第一個 then 方法的回調函數接收 resolve() 方法裏的值:

new Promise(function(resolve, reject) {
    // A mock async action using setTimeout
    setTimeout(function() { resolve(10); }, 3000);
})
.then(function(result) {
    console.log(result);
});

// From the console:
// 10

當promise被resolved時,會調用then回調方法。你也能夠鏈式調用 then 方法:

new Promise(function(resolve, reject) { 
    // A mock async action using setTimeout
    setTimeout(function() { resolve(10); }, 3000);
})
.then(function(num) { console.log('first then: ', num); return num * 2; })
.then(function(num) { console.log('second then: ', num); return num * 2; })
.then(function(num) { console.log('last then: ', num);});

// From the console:
// first then:  10
// second then:  20
// last then:  40

每個 then 方法接收到上一個 then 方法的返回值。

若是一個promise已經被resolvedthen方法有一次被調用,那麼回調函數馬上執行。若是promise被rejected,而then方法在rejection以後,那回調函數永遠不會被執行。

catch

當promise被rejected時,catch回調函數被執行:

new Promise(function(resolve, reject) {
    // A mock async action using setTimeout
    setTimeout(function() { reject('Done!'); }, 3000);
})
.then(function(e) { console.log('done', e); })
.catch(function(e) { console.log('catch: ', e); });

// From the console:
// 'catch: Done!'

reject方法裏執行什麼內容取決於你。一個常見的模式是發送一個Errorcatch方法中:

reject(Error('Data could not be found'));

Promise.all

想一下JavaScript loaders:有許多個異步任務被同時觸發,但你只想在它們都完成以後才作出迴應---這就是Promise.all的由來。Promise.all方法傳入一個promise的數組,而後在它們所有resolved以後再出發回調函數:

Promise.all([promise1, promise2]).then(function(results) {
    // Both promises resolved
})
.catch(function(error) {
    // One or more promises was rejected
});

一個完美的想象Promise.all方法做用的例子就是一次性出發多個AJAX(經過 fetch):

var request1 = fetch('/users.json');
var request2 = fetch('/articles.json');

Promise.all([request1, request2]).then(function(results) {
    // Both promises done!
});

你能夠合併都返回promise的APIs,好比fetch 和 Battery API:

Promise.all([fetch('/users.json'), navigator.getBattery()]).then(function(results) {
    // Both promises done!
});

固然,處理rejection是困難的。若是數組中有任意一個promise被rejectedcatch方法就被觸發,而且接收第一個rejection

var req1 = new Promise(function(resolve, reject) { 
    // A mock async action using setTimeout
    setTimeout(function() { resolve('First!'); }, 4000);
});
var req2 = new Promise(function(resolve, reject) { 
    // A mock async action using setTimeout
    setTimeout(function() { reject('Second!'); }, 3000);
});
Promise.all([req1, req2]).then(function(results) {
    console.log('Then: ', one);
}).catch(function(err) {
    console.log('Catch: ', err);
});

// From the console:
// Catch: Second!

Promise.race

Promise.race是個有趣的方法---不是等待全部的promise被resolved或者rejected,而是隻要數組中有一個promise被resolved或者rejectedPromise.race方法就被觸發:

var req1 = new Promise(function(resolve, reject) { 
    // A mock async action using setTimeout
    setTimeout(function() { resolve('First!'); }, 8000);
});
var req2 = new Promise(function(resolve, reject) { 
    // A mock async action using setTimeout
    setTimeout(function() { resolve('Second!'); }, 3000);
});
Promise.race([req1, req2]).then(function(one) {
    console.log('Then: ', one);
}).catch(function(one, two) {
    console.log('Catch: ', one);
});

// From the console:
// Then: Second!

一個頗有用的例子就是在有第一資源請求和第二資源請求時能夠用Promise.race

Get Used to Promises

在過去的幾年中,promise一直是一個熱點話題(若是你是Dojo Toolkit用戶的話,會是過去的10年間),如今promise已經從JavaScript框架特性的級別變成了JavaScript語言的特性。你將看到會有愈來愈多的API會基於promise來實現,這是一個很好的事情!開發者們能夠避免之前的callback hell,而且異步interactions能夠像其餘的變量同樣互相傳遞。promise花了很長的時間變成如今的樣子,而如今是應該學習promise的時候了。

譯自 JavaScript Promise API

相關文章
相關標籤/搜索