溫馨提示×

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

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

JS的Promise和Async是什么?

發(fā)布時(shí)間:2020-05-20 13:43:45 來源:億速云 閱讀:252 作者:鴿子 欄目:web開發(fā)

因?yàn)?JavaScript 是單線程語言,所以同步代碼一次只能執(zhí)行一行。這就意味著同步代碼的運(yùn)行時(shí)間超過瞬間的話,它將停止其余代碼的運(yùn)行,直到完成運(yùn)行為止。為了防止運(yùn)行時(shí)間不確定的代碼阻止其他代碼的運(yùn)行,我們需要使用異步代碼。

Promise

為此,我們可以在代碼中使用 Promise。Promise 表示流程運(yùn)行時(shí)間不確定并且結(jié)果可能是成功或失敗的對(duì)象。在 JavaScript 中創(chuàng)建一個(gè) promises,我們使用 Promise 對(duì)象的構(gòu)造函數(shù)來創(chuàng)建 promise。Promise 構(gòu)造函數(shù)接受一個(gè)擁有 resolve 和 reject 參數(shù)的執(zhí)行函數(shù)。兩個(gè)參數(shù)也都是函數(shù),它們讓我們可以回調(diào) promise 完成(成功調(diào)用得到返回值)或者拒絕(返回錯(cuò)誤值并標(biāo)記 promise 失?。?。函數(shù)的返回值被忽略。因此,promise 只能返回 promise。

例如,我們?cè)?JavaScript 定義一個(gè)承諾像下面的代碼:

const promise = new Promise((resolve, reject) => {  
  setTimeout(() => resolve('abc'), 1000);  
});

上面的代碼會(huì)創(chuàng)建一個(gè)一秒后返回 abc 的 promise。因?yàn)槲覀冞\(yùn)行的 setTimeout 是在 executor 函數(shù)內(nèi)一秒后返回值為 abc 的完成 promise,所以它是異步代碼。我們無法在 setTimeout 的回調(diào)函數(shù)中返回值 abc,因此我們必須調(diào)用 resolve('abc') 來獲取解析后的值。我們可以使用 then 函數(shù)來訪問已完成 promise 的返回值。then 函數(shù)接受一個(gè)回調(diào)函數(shù),該回調(diào)函數(shù)將已完成的 promise 的返回值作為參數(shù)。你可以得到你想要的值。例如,我們可以這么做:

const promise = new Promise((resolve, reject) => {  
  setTimeout(() => resolve('abc'), 1000);  
});
promise.then((val) => {  
  console.log(val);  
})

當(dāng)我們運(yùn)行上方代碼,我們應(yīng)該可以得到記錄 abc。正如我們看到的,promise 會(huì)在其完成后調(diào)用 resolve 函數(shù)時(shí)提供值。

一個(gè) promise 有三種狀態(tài)。初始狀態(tài),promise 既沒有成功也沒有失敗。完成狀態(tài),意味著操作成功完成?;蛘呤鞘顟B(tài),意味著 promise 操作失敗。

一個(gè)掛起的 promise 可以通過返回值完成或者返回錯(cuò)誤失敗。當(dāng) promise 完成后,then 函數(shù)將得到相應(yīng)的返回值,并傳遞給 then 函數(shù)的回調(diào)函數(shù)來調(diào)用。如果 promise 失敗后,我們可以選擇使用 catch 函數(shù)來捕獲錯(cuò)誤,該函數(shù)還可以傳遞給回調(diào)函數(shù)一個(gè)錯(cuò)誤。then 和 catch 都返回一個(gè) promise,因此它們可以一起鏈?zhǔn)绞褂谩?/p>

例如,我們可以這么寫:

const promise = (num) => {  
  return new Promise((resolve, reject) => {  
    setTimeout(() => {  
      if (num === 1) {  
        resolve('resolved')  
      } else {  
        reject('rejected')  
      }  
    }, 1000);  
  });  
}
promise(1)  
  .then((val) => {  
    console.log(val);  
  })  
  .catch((error) => {  
    console.log(error);  
  })promise(2)  
  .then((val) => {  
    console.log(val);  
  })  
  .catch((error) => {  
    console.log(error);  
  })

在上面的代碼中,我們有一個(gè)函數(shù) promise,它返回一個(gè) JavaScript promise,其在 num 為 1 時(shí)以 resolved 值完成承諾,在 num 不為 1 時(shí)通過錯(cuò)誤 rejected 拒絕承諾。因此我們運(yùn)行:

promise(1)  
  .then((val) => {  
    console.log(val);  
  })  
  .catch((error) => {  
    console.log(error);  
  })

然后 then 函數(shù)運(yùn)行,由于 num 為 1,promise(1) 函數(shù)調(diào)用會(huì)返回 promise 完成,并且解析后的值在 val 中是可用的。因此當(dāng)我們運(yùn)行 console.log(val),我們會(huì)得到 resolved。當(dāng)我們運(yùn)行下面的代碼:

promise(2)  
  .then((val) => {  
    console.log(val);  
  })  
  .catch((error) => {  
    console.log(error);  
  })

catch 會(huì)運(yùn)行,因?yàn)?promise(2) 函數(shù)調(diào)用失敗返回 promise,并且被拒絕的錯(cuò)誤值可用并設(shè)置為錯(cuò)誤。因此我們運(yùn)行 console.log(error) 會(huì)得到 rejected 輸出。

一個(gè) JavaScript promise 對(duì)象有一下屬性:length 和 prototype。 length 是構(gòu)造器參數(shù)的數(shù)量,其設(shè)置為 1,也總是只有一個(gè)。prototype 屬性表示 promise 對(duì)象的原型。

promise 還有一個(gè) finally 方法無論 promise 完成還是失敗都運(yùn)行的代碼。finally 方法接受一個(gè)回調(diào)函數(shù)作為參數(shù),可以運(yùn)行任何你想運(yùn)行的代碼, 并且無論 promise 運(yùn)行結(jié)果如何,都可以執(zhí)行。例如,我們運(yùn)行:

Promise.reject('error')  
  .then((value) => {  
    console.log(value);  
  })  
  .catch((error) => {  
    console.log(error);  
  })  
  .finally(() => {  
    console.log('finally runs');  
  })

然后我們得到 error 和 finally runs 的記錄,因?yàn)樵嫉?promise 得到 error 而拒絕。然后運(yùn)行 finally 方法中的所有代碼。

使用 promise 最主要的好處是編寫的異步代碼,我們可以使用 promise 順序運(yùn)行它們。為此,我們可以使用 then 函數(shù)鏈?zhǔn)?promise。then 函數(shù)在 promise 完成后接收一個(gè)回調(diào)函數(shù)并運(yùn)行它。在 promise 拒絕后,它還接受第二個(gè)參數(shù)。鏈?zhǔn)绞褂?promise,我們必須將其第一個(gè)回調(diào)函數(shù) then 函數(shù)返回另外一個(gè) promise。如果我們不想將另外一個(gè) promise 鏈接到現(xiàn)有的 promise,我們可以返回其他值,就像沒有一樣。我們可以返回一個(gè)值,該值將在下一個(gè) then 函數(shù)中可獲取到。它還可以拋出一個(gè)錯(cuò)誤。然后 then 返回的 promise 將被拒絕,并拋出錯(cuò)誤。它還可以返回已經(jīng)完成或拒絕的 promise,擋在其后鏈接的 then 函數(shù)時(shí)將獲得其完成后的值,或者 catch 函數(shù)的回調(diào)中獲得錯(cuò)誤原因。

例如,我們可以這樣寫:

Promise.resolve(1)  
  .then(val => {  
    console.log(val);  
    return Promise.resolve(2)  
  })  
  .then(val => {  
    console.log(val);  
  })Promise.resolve(1)  
  .then(val => {  
    console.log(val);  
    return Promise.reject('error')  
  })  
  .then(val => {  
    console.log(val);  
  })  
  .catch(error => console.log(error));Promise.resolve(1)  
  .then(val => {  
    console.log(val);  
    throw new Error('error');  
  })  
  .then(val => {  
    console.log(val);  
  })  
  .catch(error => console.log(error));

在第一個(gè)例子中,我們鏈?zhǔn)秸{(diào)用 promise,并且都 resolve 一個(gè)值。所有的 promise 都準(zhǔn)備返回為值。在第二個(gè)和最后一個(gè)例子中,我們拒絕了第二個(gè) promise 或拋出一個(gè)錯(cuò)誤。它們都做了同樣的事情。第二個(gè) promise 被拒絕了,并且錯(cuò)誤原因會(huì)被回調(diào)函數(shù) catch 函數(shù)記錄下來。我們還可以鏈接掛起狀態(tài)的 promise,如下方代碼所示:

const promise1 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(1), 1000);  
});
const promise2 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(2), 1000);  
});
promise1  
  .then(val => {  
    console.log(val);  
    return promise2;  
  })  
  .then(val => {  
    console.log(val);  
  })  
  .catch(error => console.log(error));

回調(diào)函數(shù) then 函數(shù)返回了 promise2,這是一個(gè)掛起狀態(tài)的 promise。

方法

JavaScript 的 promise 有以下方法。

Promise.all (可迭代對(duì)象)

Promise.all 接受一個(gè)可迭代對(duì)象,該對(duì)象允許我們?cè)谀承┯?jì)算機(jī)上并行運(yùn)行多個(gè) promise,并在其他計(jì)算機(jī)上連續(xù)運(yùn)行多個(gè) promise。這對(duì)于運(yùn)行多個(gè)不依賴于彼此的值的 promise 非常方便。它接受一個(gè)包含 promise 的列表 (通常是一個(gè)數(shù)組) 的可迭代對(duì)象,然后返回一個(gè) Promise,這個(gè) Promise 在可迭代對(duì)象中的 promise 被解析時(shí)解析。

例如,我們像下面這樣寫代碼,使用 Promise.all 來運(yùn)行多個(gè) promise:

const promise1 = Promise.resolve(1);  
const promise2 = 2;  
const promise3 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(3), 1000);  
});  
Promise.all([promise1, promise2, promise3])  
  .then((values) => {  
    console.log(values);  
  });

如果我們運(yùn)行了上方的代碼,然后 console.log 應(yīng)該會(huì)記錄下 [1,2,3]。如我們所見,只有所有的 promise 都完成后返回它的解析值。如果其中的拒絕了,我們不會(huì)得到任何解析值。相反,我們將得到由被拒絕的 promise 返回的任何錯(cuò)誤值。它將會(huì)在第一個(gè)被拒絕的 promise 處停止,并且發(fā)送值給回調(diào)函數(shù) catch 函數(shù)。例如,如果我們這樣:

const promise1 = Promise.resolve(1);  
const promise2 = Promise.reject(2);  
const promise3 = new Promise((resolve, reject) => {  
  setTimeout(() => reject(3), 1000);  
});  
Promise.all([promise1, promise2, promise3])  
  .then((values) => {  
    console.log(values);  
  })  
  .catch(error => {  
    console.log(error);  
  });

然后我們可以在回調(diào)函數(shù) catch 函數(shù)的 console.log 中得到兩個(gè)記錄。

Promise.allSettled

Promise.allSettled 返回一個(gè) promise,該 promise 的解析在所有的 promise 解析完或拒絕后。它接受帶有一組 promise 的可迭代對(duì)象,例如,一個(gè) promise 數(shù)組。返回的 promise 的解析值是每個(gè) promise 的最終狀態(tài)的數(shù)組。例如,假設(shè)我們有:

const promise1 = Promise.resolve(1);  
const promise2 = Promise.reject(2);  
const promise3 = new Promise((resolve, reject) => {  
  setTimeout(() => reject(3), 1000);  
});  
Promise.allSettled([promise1, promise2, promise3])  
  .then((values) => {  
    console.log(values);  
  })

若我們運(yùn)行上方代碼,我們將獲得一個(gè)包含三個(gè)條目的數(shù)組,每個(gè)條目都是一個(gè)對(duì)象,該對(duì)象有已經(jīng)完成 promise 的 status 和 value 屬性以及被拒絕的 promise 的 status 和 reason 屬性。例如,上面的代碼會(huì)記錄 {status: “fulfilled”, value: 1},{status: “rejected”, reason: 2},{status: “rejected”, reason: 3}。 fulfilled 狀態(tài)記錄的是成功的 promise,rejected 狀態(tài)為被拒絕的 promise。

Promise.race

Promise.race 方法返回一個(gè) promise,該 promise 會(huì)解析首先完成的 promise 的解析值。它接受一個(gè)帶有 promise 集合的可迭代對(duì)象,例如,一個(gè) promise 數(shù)組。如果傳入的可迭代對(duì)象為空,則返回的 promise 將一直掛起。若可迭代對(duì)象包含一個(gè)或多個(gè)非 promise 值或者已經(jīng)完成的 promise,Promise.race 將會(huì)返回這些條目中的第一個(gè)。例如,我們有:

const promise1 = Promise.resolve(1);  
const promise2 = Promise.resolve(2);  
const promise3 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(3), 1000);  
});  
Promise.race([promise1, promise2, promise3])  
  .then((values) => {  
    console.log(values);  
  })

然后我們看到 1 會(huì)被記錄 。那是因?yàn)?promise1 是第一個(gè)被解析的,那是因?yàn)樗窃谙乱恍羞\(yùn)行之前就被解析了。同樣,如果我們的數(shù)組中有一個(gè)非 promise 值作為參數(shù)進(jìn)行傳入,如下代碼所示:

const promise1 = 1;  
const promise2 = Promise.resolve(2);  
const promise3 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(3), 1000);  
});  
Promise.race([promise1, promise2, promise3])  
  .then((values) => {  
    console.log(values);  
  })

然后我們會(huì)得到相同的記錄,因?yàn)樗俏覀儌鬟f給 Promise.race 方法的數(shù)組中的非 promise 值。同步代碼始終運(yùn)行在異步代碼之前,而不論同步代碼在哪里。如果我們有:

const promise1 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(1), 2000);  
});
const promise2 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(2), 1000);  
});
const promise3 = 3;  
Promise.race([promise1, promise2, promise3])  
  .then((values) => {  
    console.log(values);  
  })

然后我們記錄下 3,因?yàn)?setTimeout 將回調(diào)函數(shù)放入隊(duì)列中以便稍后運(yùn)行,所以它將比同步代碼更晚執(zhí)行。

最后,如果我們有:

const promise1 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(1), 2000);  
}); 
const promise2 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(2), 1000);  
});
Promise.race([promise1, promise2])  
  .then((values) => {  
    console.log(values);  
  })

然后我們?cè)诳刂婆_(tái)中得到記錄 2,因?yàn)樵谝幻虢馕龅?promise 要比兩秒解析的 promise 更早解析。

Promise.reject

Promise.reject 返回一個(gè)因某種原因拒絕的 promise。拒絕帶有 Error 的實(shí)例對(duì)象的 promise 非常有用。例如,如果我們有以下代碼:

Promise.reject(new Error('rejected'))  
  .then((value) => {  
    console.log(value);  
  })  
  .catch((error) => {  
    console.log(error);  
  })

然后我們得到 rejected 記錄。

Promise.resolve

Promise.resolve 返回一個(gè)已解析為傳入 resolve 函數(shù)參數(shù)的值的 promise。我們也可以傳遞一個(gè)帶有 then 屬性的對(duì)象,它的值是 promise 的回調(diào)函數(shù)。如果該值具有 then 方法,則將使用 then 函數(shù)完成 promise。也就是說,then 函數(shù)值的第一個(gè)參數(shù)與 resolve 相同,以及第二個(gè)參數(shù)與 reject 相同。例如,我們可以編寫如下代碼:

Promise.resolve(1)  
  .then((value) => {  
    console.log(value);  
  })

然后我們得到 1 記錄,因?yàn)?1 是我們傳遞給 resolve 函數(shù)來返后具有解析值 1 的承諾的值。

如果我們傳入的對(duì)象內(nèi)部帶有 then 方法,如下代碼所示:

Promise.resolve({  
    then(resolve, reject) {  
      resolve(1);  
    }  
  })  
  .then((value) => {  
    console.log(value);  
  })

然后我們得到記錄的值 1。這是因?yàn)?Promise.resolve 函數(shù)將運(yùn)行 then 函數(shù),設(shè)置為 “then” 屬性的函數(shù)的 “resolve” 參數(shù)將被假定為承諾中稱為 “resolve” 函數(shù)的函數(shù)。并將該函數(shù)的 resolve 參數(shù)設(shè)置為 then 屬性可以看作 promise 中一個(gè)叫做 resolve 函數(shù)。如果我們將傳入 then 中的對(duì)象替換為 inject 函數(shù),然后我們就可以得到被拒絕的 promise。代碼如下所示:

Promise.resolve({  
    then(resolve, reject) {  
      reject('error');  
    }  
  })  
  .then((value) => {  
    console.log(value);  
  })  
  .catch((error) => {  
    console.log(error);  
  })

在上面的代碼中,我們會(huì)得到 error 記錄,這是因?yàn)?promise 被拒絕了。

Async 和 Await

使用 async 和 await,我們可以縮短 promise 代碼。使用 async 和 await 之前前,我們必須得用 then 函數(shù)并且在 then 函數(shù)中放入回調(diào)函數(shù)作為所有的參數(shù)。這就使得我們有很多 promise 時(shí)代碼冗長至極。相反,我們可以使用 async 和 await 語法來替代 then 函數(shù)以及相關(guān)回調(diào)。例如,我們可將以下代碼縮短為:

const promise1 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(1), 2000);  
});
const promise2 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(2), 1000);  
});
promise1  
  .then((val1) => {  
    console.log(val1);  
    return promise2;  
  })  
  .then((val2) => {  
    console.log(val2);  
  })

寫成:

const promise1 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(1), 2000);  
});  
const promise2 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(2), 1000);  
});
(async () => {  
  const val1 = await promise1;  
  console.log(val1)  
  const val2 = await promise2;  
  console.log(val2)  
})()

我們使用 await 來替換 then 和回調(diào)函數(shù)。然后,我們就可以將每個(gè) promise 的解析值分配為變量。注意,如果我們?yōu)?promise 代碼使用 await,那么我們必須像上例那樣添加 async 到函數(shù)簽名中。為了捕獲錯(cuò)誤,我們使用 catch 子句取代鏈?zhǔn)?catch 函數(shù)。另外,我們沒有在底部鏈?zhǔn)秸{(diào)用 finally 函數(shù)以在 promise 結(jié)束時(shí)運(yùn)行代碼,而是在 catch 子句后使用 finally 子句。

例如,我們可以這樣寫:

const promise1 = new Promise((resolve, reject) => {  
  setTimeout(() => resolve(1), 2000);  
});  
const promise2 = new Promise((resolve, reject) => {  
  setTimeout(() => reject('error'), 1000);  
});
(async () => {  
  try {  
    const val1 = await promise1;  
    console.log(val1)  
    const val2 = await promise2;  
    console.log(val2)  
  } catch (error) {  
    console.log(error)  
  } finally {  
    console.log('finally runs');  
  }})()

在上面的代碼中,我們獲得了分配給變量的 promise 的解析值,而不是在 then 函數(shù)的回調(diào)中獲取值,例如在 const response = await promise1 上面的一行。另外,我們使用 try...catch...finally 塊來捕獲被拒絕的 promise 的錯(cuò)誤,以及 finally 子句替代 finally 函數(shù),其無論 promise 執(zhí)行結(jié)果如何,該代碼都可以運(yùn)行。

想其他使用 promise 的函數(shù)一樣,async 函數(shù)始終返回 promise,并且不能返回其他任何東西。在上面的示例中,我們證明了與使用帶有回調(diào)函數(shù)作為參數(shù)傳遞的 then 函數(shù)相比,我們可以更短的方式使用 promise。

結(jié)束語

使用 promise,讓我們編寫異步代碼更容易。promise 是表示一個(gè)處理的運(yùn)行時(shí)間不確定并且結(jié)果會(huì)成功也會(huì)失敗的對(duì)象。在 JavaScript 中創(chuàng)建一個(gè) promises,我們使用 Promise 對(duì)象,該對(duì)象是用于創(chuàng)建 promise 的構(gòu)造函數(shù)。

Promise 構(gòu)造函數(shù)接受一個(gè)擁有 resolve 和 reject 參數(shù)的執(zhí)行函數(shù)。兩個(gè)參數(shù)都是函數(shù),它們讓我們可以回調(diào) promise 完成(成功調(diào)用得到返回值)或者拒絕(返回錯(cuò)誤值并標(biāo)記 promise 失敗)。 The 函數(shù)的返回值被忽略。因此,promise 只能返回 promise。

因?yàn)?promise 返回 promise,所以 promise 是可鏈?zhǔn)秸{(diào)用。promise 的 then 函數(shù)可以拋出一個(gè)錯(cuò)誤,返回解析值,或者其他 promise(掛起、已完成或已拒絕的 promise)。

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

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

AI