溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何在ES6中使用promise 和async函數(shù)

發(fā)布時(shí)間:2021-03-17 14:50:35 來源:億速云 閱讀:219 作者:Leah 欄目:web開發(fā)

如何在ES6中使用promise 和async函數(shù)?針對(duì)這個(gè)問題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡(jiǎn)單易行的方法。

promise 基本用法

Promise 對(duì)象是一個(gè)構(gòu)造函數(shù),用來生成 Promise 實(shí)例。Promise 構(gòu)造函數(shù)接受一個(gè)函數(shù)作為參數(shù),該函數(shù)的兩個(gè)參數(shù)分別是 resolve 和 reject。

resolve 函數(shù)的作用是,在異步操作成功時(shí)調(diào)用(Promise 對(duì)象的狀態(tài)從 pending 變?yōu)?fulfilled),并將異步操作的結(jié)果,作為參數(shù)傳遞出去。

reject 函數(shù)的作用是,在異步操作失敗時(shí)調(diào)用(Promise對(duì)象的狀態(tài)從 pending 變?yōu)?rejected),并將異步操作報(bào)出的錯(cuò)誤,作為參數(shù)傳遞出去。

const funPromise = function(options) {
 return new Promise(function(resolve, reject) {
  if (/* 異步操作成功 */){
   resolve(result);
  } else {
   reject(error);
  }
 });
}

resolve 函數(shù)的參數(shù)除了正常的值以外,還可能是另一個(gè) Promise 實(shí)例,此時(shí),初始 promise 的最終狀態(tài)根據(jù)傳入的新的 Promise 實(shí)例決定。

reject 方法的作用,相當(dāng)于拋出錯(cuò)誤。等同于 throw new Error('error')。

Promise.prototype.then()

Promise 實(shí)例具有 then 方法,它的作用是為 Promise 實(shí)例添加狀態(tài)改變時(shí)的回調(diào)函數(shù),即 Promise 實(shí)例生成以后,用 then 方法分別指定 fulfilled 狀態(tài)和 rejected 狀態(tài)的回調(diào)函數(shù)。

funPromise().then(function(result) {
 // fulfilled
}, function(error) {
 // rejected
})

then 方法可以接受兩個(gè)回調(diào)函數(shù)作為參數(shù)。第一個(gè)回調(diào)函數(shù)是 Promise 對(duì)象的狀態(tài)變?yōu)?fulfilled 時(shí)調(diào)用,第二個(gè)回調(diào)函數(shù)是 Promise 對(duì)象的狀態(tài)變?yōu)?rejected 時(shí)調(diào)用。其中,第二個(gè)函數(shù)是可選的,不一定要提供。這兩個(gè)函數(shù)都接受 Promise 對(duì)象傳出的值作為參數(shù)。

then 方法返回的是一個(gè)新的 Promise 實(shí)例(注意,不是原來那個(gè) Promise 實(shí)例)。因此可以采用鏈?zhǔn)綄懛?,?then 方法后面再調(diào)用另一個(gè) then 方法來處理上一個(gè) then 方法中 return 的結(jié)果。

funPromise().then(function(result) {
 return result.data;
}).then(function(data) {
 // fulfilled
});

上面的代碼使用 then 方法,依次指定了兩個(gè)回調(diào)函數(shù)。第一個(gè)回調(diào)函數(shù)完成以后,會(huì)將返回結(jié)果作為參數(shù),傳入第二個(gè)回調(diào)函數(shù)。并且,第一個(gè) then 返回的結(jié)果也可以是另一個(gè)異步操作的 Promise 對(duì)象,這時(shí)后一個(gè) then 函數(shù),就會(huì)等待該 Promise 對(duì)象的狀態(tài)發(fā)生變化,才會(huì)被調(diào)用。

funPromise().then(
 (result) => { return funPromise(result); }
).then(
 (data) => { /* fulfilled */ },
 (error) => { /* rejected */ }
);

上面代碼中,第一個(gè) then 方法指定的回調(diào)函數(shù),返回的是另一個(gè) Promise 對(duì)象。這時(shí),第二個(gè) then 方法指定的回調(diào)函數(shù),就會(huì)等待這個(gè)新的 Promise 對(duì)象狀態(tài)發(fā)生變化。如果變?yōu)?fulfilled,就調(diào)用第一個(gè)回調(diào)函數(shù),如果狀態(tài)變?yōu)?rejected,就調(diào)用第二個(gè)回調(diào)函數(shù)。

Promise.prototype.catch()

Promise 實(shí)例具有 catch 方法,它的作用是為 Promise 實(shí)例添加狀態(tài)改變?yōu)?rejected 狀態(tài)的回調(diào)函數(shù),也就是 then 方法的第二個(gè)函數(shù)的替代寫法。

funPromise().then(function(result) {
 // fulfilled
}).catch(function(error) {
 // 處理 funPromise 和之前 then 回調(diào)函數(shù)運(yùn)行時(shí)發(fā)生的錯(cuò)誤
});

Promise 對(duì)象的錯(cuò)誤具有“冒泡”性質(zhì),會(huì)一直向后傳遞,直到被捕獲為止。也就是說,無(wú)論前面有多少個(gè) then 函數(shù),其中的錯(cuò)誤總是會(huì)被下一個(gè) catch 語(yǔ)句捕獲。

funPromise().then(function(result) {
 return funPromise(result);
}).then(function(data) {
 // fulfilled
}).catch(function(error) {
 // 處理前面三個(gè) Promise 產(chǎn)生的錯(cuò)誤
});

一般來說,不要在 then 方法里面定義 rejected 狀態(tài)的回調(diào)函數(shù)(即 then 的第二個(gè)參數(shù)),總是使用 catch 方法,因?yàn)檫@種寫法可以捕獲前面 then 方法執(zhí)行中的錯(cuò)誤。

catch 方法返回的還是一個(gè) Promise 對(duì)象,并且 catch 中如果沒有拋出任何其它錯(cuò)誤,那么該 Promise 對(duì)象則是 resolved 狀態(tài)。而且后面還可以接著調(diào)用 then 方法,但是前面的 catch 不能捕獲后面的 then 中的錯(cuò)誤,所以盡量 catch 都寫在最后。

Promise.all()

Promise.all() 方法用于將多個(gè) Promise 實(shí)例,包裝成一個(gè)新的 Promise 實(shí)例。其接受一個(gè)數(shù)組作為參數(shù),數(shù)組中的值都是 Promise 實(shí)例,如果不是,就會(huì)先調(diào)用 Promise.resolve() 方法,將參數(shù)轉(zhuǎn)為 Promise 實(shí)例,再進(jìn)一步處理。

const p = Promise.all([funPromise(1), funPromise(2), funPromise(3)]);

p 的狀態(tài)由數(shù)組中的值決定,分成兩種情況。

  • 數(shù)組中 Primise 實(shí)例的狀態(tài)都變成 fulfilled,p 的狀態(tài)才會(huì)變成 fulfilled,此時(shí)數(shù)組中實(shí)例的返回值組成一個(gè)數(shù)組,傳遞給 p 的回調(diào)函數(shù)。

  • 只要數(shù)組的實(shí)例之中有一個(gè)被 rejected,p 的狀態(tài)就變成 rejected,此時(shí)第一個(gè)被 reject 的實(shí)例的返回值,也就是報(bào)錯(cuò)信息,會(huì)傳遞給 p 的回調(diào)函數(shù)。

p.then(function (results) {
 // 全部 fulfilled,results 是個(gè)數(shù)組,里面是每個(gè)實(shí)例的返回結(jié)果
}).catch(function(error){
 // 其中有一個(gè)變?yōu)?nbsp;rejected
});

注意,如果作為參數(shù)的 Promise 實(shí)例,自己定義了 catch 方法,那么它一旦被 rejected,并不會(huì)觸發(fā) Promise.all() 的 catch 方法。

應(yīng)用

用 Promise 對(duì)象實(shí)現(xiàn) Ajax。

const getAjax = function(url) {
 const promise = new Promise(function(resolve, reject){
  const handler = function() {
   if (this.readyState === 4 && this.status === 200) {
    resolve(this.response);
   } else {
    reject(new Error(this.statusText));
   }
  };
  const xhr = new XMLHttpRequest();
  xhr.open("GET", url);
  xhr.onreadystatechange = handler;
  xhr.responseType = "json";
  xhr.setRequestHeader("Accept", "application/json");
  xhr.send();
 });
 return promise;
};

getAjax("/test.json").then(function(json) {
 console.log('Contents: ' + json);
}, function(error) {
 console.error('出錯(cuò)了', error);
});

async / await 基本用法

當(dāng) async 函數(shù)執(zhí)行的時(shí)候,一旦遇到 await 就會(huì)先等到 await 后的異步操作完成,再接著執(zhí)行函數(shù)體內(nèi)之后的語(yǔ)句。

async 函數(shù)返回一個(gè) Promise 對(duì)象,可以使用 then 方法添加回調(diào)函數(shù)。async 函數(shù)內(nèi)部 return 語(yǔ)句返回的值,會(huì)成為 then 方法回調(diào)函數(shù)的參數(shù)。

async function f() {
 return 'hello dora';
}

f().then(v => console.log(v))  // "hello dora"

async 函數(shù)內(nèi)部拋出錯(cuò)誤,會(huì)導(dǎo)致返回的 Promise 對(duì)象變?yōu)?rejected 狀態(tài)。拋出的錯(cuò)誤對(duì)象會(huì)被 catch 方法回調(diào)函數(shù)接收到。

async function f() {
 throw new Error('出錯(cuò)了');
}

f().catch( e => console.log(e)) // Error: 出錯(cuò)了

await 命令

正常情況下,await 命令后面是一個(gè) Promise 對(duì)象,返回該對(duì)象的結(jié)果。如果不是 Promise 對(duì)象,就直接返回對(duì)應(yīng)的值。

async function f() {
 return await 123;       // 等同于 return 123;
}

f().then(v => console.log(v)) // 123

await 命令后面的 Promise 對(duì)象如果變?yōu)?rejected 狀態(tài),則錯(cuò)誤會(huì)被 catch 方法的回調(diào)函數(shù)接收到。

任何一個(gè) await 語(yǔ)句后面的 Promise 對(duì)象變?yōu)?rejected 狀態(tài),那么整個(gè) async 函數(shù)就會(huì)中斷執(zhí)行。

有時(shí),我們希望即使前一個(gè)異步操作失敗,也不要中斷后面的異步操作,有兩個(gè)解決辦法:

第一種方法是可以將 await 放在 try...catch 結(jié)構(gòu)里面,這樣不管這個(gè)異步操作是否成功,后面的代碼都會(huì)執(zhí)行。

async function f() {
 try {
  await Promise.reject('出錯(cuò)了');
 } catch(e) { }
 return await Promise.resolve('hello dora');
}

f().then(v => console.log(v))  // hello dora

另一種方法是 await 后面的 Promise 對(duì)象再跟一個(gè) catch 方法,處理前面可能出現(xiàn)的錯(cuò)誤。

async function f() {
 await Promise.reject('出錯(cuò)了').catch(e => console.log(e));
 return await Promise.resolve('hello dora');
}

f().then(v => console.log(v))
// 出錯(cuò)了
// hello dora

使用注意點(diǎn)

1. 錯(cuò)誤處理

前面已經(jīng)說過,await 命令后面的 Promise 對(duì)象,運(yùn)行結(jié)果可能是 rejected,所以防止出錯(cuò)的方法,就是最好把 await 命令放在 try...catch 代碼塊中。如果有多個(gè) await 命令,可以統(tǒng)一放在 try...catch 結(jié)構(gòu)中,如果只有一個(gè) await,可以使用上例中的 catch 捕獲 await 后面的 promise 拋出的錯(cuò)誤。

const superagent = require('superagent');
const NUM_RETRIES = 3;

async function test() {
 let i;
 for (i = 0; i < NUM_RETRIES; i++) {
  try {
   await superagent.get('/api/xxx');
   break;
  } catch(err) {}
 }
}

test();

上面代碼中,使用 try...catch 結(jié)構(gòu),實(shí)現(xiàn)多次重復(fù)嘗試。如果 await 操作成功,就會(huì)使用 break 語(yǔ)句退出循環(huán);如果失敗,會(huì)被 catch 語(yǔ)句捕捉,然后進(jìn)入下一輪循環(huán)。

2. 多個(gè) await 異步操作并發(fā)執(zhí)行

多個(gè) await 命令后面的異步操作,如果不存在繼發(fā)關(guān)系(即互不依賴),最好讓它們同時(shí)觸發(fā),以縮短程序的執(zhí)行時(shí)間。

// 寫法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 寫法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

3. forEach 等數(shù)組遍歷方法的參數(shù)為 async 函數(shù)時(shí)是并發(fā)執(zhí)行的

只有 async 函數(shù)內(nèi)部是繼發(fā)執(zhí)行,外部不受影響,因此 forEach()、map() 等數(shù)組遍歷方法的參數(shù)改成 async 時(shí)是并發(fā)執(zhí)行的。

function dbFuc() { //這里不需要 async
 let docs = [{}, {}, {}];

 // 會(huì)得到錯(cuò)誤結(jié)果
 docs.forEach(async (doc)=> {
  await funPromise(doc);
 });
}

上面代碼會(huì)得到錯(cuò)誤結(jié)果,原因是這時(shí)三個(gè) funPromise(doc) 操作是并發(fā)執(zhí)行的,也就是同時(shí)執(zhí)行,而不是繼發(fā)執(zhí)行。因此正確的寫法是采用 for 循環(huán)。

async function dbFuc() {
 let docs = [{}, {}, {}];

 for (let doc of docs) {
  await funPromise(doc);
 }
}

如果需要并發(fā)執(zhí)行,可使用 Promise.all() 方法。

async function dbFuc() {
 let docs = [{}, {}, {}];
 let promises = docs.map((doc) => funPromise(doc));

 let results = await Promise.all(promises);
 return results;
}

有一組異步操作,需要按照順序完成。

async function logInOrder(urls) {
 // 并發(fā)讀取遠(yuǎn)程URL
 const textPromises = urls.map(async url => {
  const response = await fetch(url);
  return response.text();
 });

 // 按次序輸出
 for (const textPromise of textPromises) {
  console.log(await textPromise);
 }
}

關(guān)于如何在ES6中使用promise 和async函數(shù)問題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI