欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript中Promise的使用方法實(shí)例

 更新時(shí)間:2022年05月12日 11:01:56   作者:An595  
現(xiàn)在不會(huì)用Promise都不好意思說(shuō)自己是前端,下面這篇文章主要給大家介紹了關(guān)于JavaScript中Promise使用的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

我還記得我剛開(kāi)始學(xué)習(xí)JavaScript的Promise很多概念都不懂很"懵逼", 現(xiàn)在已經(jīng)工作有小半年時(shí)間了, 整理整理筆記和個(gè)人在工作中的使用情況, 寫(xiě)個(gè)文章記錄一下(PS只記錄怎么使用, 原理太深?yuàn)W功力不夠), 如果有不對(duì)的地方前輩勿噴??

Promise簡(jiǎn)介

Promise也稱(chēng)期約, 是ES6推出的一個(gè)異步解決方案, 可以有效的解決異步函數(shù)嵌套太深("回調(diào)地獄")的問(wèn)題

什么是回調(diào)地獄?

假設(shè)有個(gè)需求需要獲取用戶(hù)的指定數(shù)據(jù)用戶(hù)數(shù)據(jù)3這個(gè)數(shù)據(jù)依賴(lài)用戶(hù)數(shù)據(jù)2用戶(hù)數(shù)據(jù)2又依賴(lài)于用戶(hù)數(shù)據(jù)1, 所以正確的數(shù)據(jù)獲取數(shù)據(jù)順序?yàn)? 用戶(hù)數(shù)據(jù)1-->用戶(hù)數(shù)據(jù)2-->用戶(hù)數(shù)據(jù)3, 模擬一下使用回調(diào)函數(shù)的寫(xiě)法如下:

Node接口:

const router = require('express').Router();

const data1 = { data: "用戶(hù)數(shù)據(jù)1" };
router.get('/testData1', (req, res) => {
    res.json(data1);
})

const data2 = { data: "用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2" };
router.get('/testData2', (req, res) => {
    if (req.query.data === data1.data) {
        res.json(data2);
    } else {
        res.status(401).json("參數(shù)錯(cuò)誤");
    }
})

router.get('/testData3', (req, res) => {
    if (req.query.data === data2.data) {
        res.json({ data: "用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2,用戶(hù)數(shù)據(jù)3" });
    } else {
        res.status(401).json("參數(shù)錯(cuò)誤");
    }
})

module.exports = router;

前端請(qǐng)求代碼:

// 簡(jiǎn)單封裝的 XMLHttpRequest 請(qǐng)求函數(shù)
const baseUrl = "http://localhost:8888/test";
const request = (url, cb) => {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', baseUrl + url);
  xhr.send();
  xhr.onreadystatechange = () => {
    const { readyState, status, response } = xhr;
    if (readyState === 4) {
      if (status >= 200 && status <= 299) {
        cb(JSON.parse(response));
      } else {
        throw new Error(response);
      }
    }
  }
}

// 因?yàn)橄乱粋€(gè)請(qǐng)求需要上一個(gè)請(qǐng)求的數(shù)據(jù)所以需要一個(gè)回調(diào)函數(shù)嵌套一個(gè)回調(diào)函數(shù)
request("/testData1", res1 => {
  console.log(res1); // => {data: '用戶(hù)數(shù)據(jù)1'}
  request(`/testData2?data=${res1.data}`, res2 => {
    console.log(res2); // => {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2'}
    request(`/testData3?data=${res2.data}`, res3 => {
      console.log("需求需要的數(shù)據(jù)", res3); // => 需求需要的數(shù)據(jù) {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2,用戶(hù)數(shù)據(jù)3'}
      // ....
    })
  })
})

這個(gè)代碼看著就頭大, 如果需求復(fù)雜的話, 我只能用一張圖表示(這張圖片我也不記得從哪里保存的忘了??)

這種一個(gè)回調(diào)嵌套一個(gè)回調(diào)的代碼可讀性和可維護(hù)性都很差, 被稱(chēng)為"回調(diào)地獄", 而Promise的出現(xiàn)就可以很好的解決這個(gè)問(wèn)題

Promise的特點(diǎn)

Promise對(duì)象有一個(gè)狀態(tài), 這個(gè)狀態(tài)不受外界影響, 狀態(tài)一共分為3種:

  • Pending狀態(tài) (進(jìn)行中(又稱(chēng)待定)) 初始狀態(tài)
  • Fulfilled狀態(tài) (成功(又稱(chēng)兌現(xiàn)))
  • Rejected狀態(tài)(失敗(又稱(chēng)拒絕))

關(guān)于Promise的狀態(tài)叫法下文統(tǒng)一使用成功,失敗

一旦Promise對(duì)象的狀態(tài)改變(成功或失敗)就不會(huì)再變, 是單向的:

Pending(進(jìn)行中) -> Fulfilled(成功)

Pending(進(jìn)行中) -> Rejected(失敗)

創(chuàng)建Promise實(shí)例

創(chuàng)建Promise實(shí)例需要newPromise構(gòu)造函數(shù), 該構(gòu)造函數(shù)接受一個(gè)函數(shù)(處理器函數(shù))作為參數(shù), 該函數(shù)會(huì)收到兩個(gè)參數(shù), 這兩個(gè)參數(shù)分別是resolve和reject(叫什么都行一般還是要語(yǔ)義化名稱(chēng))它們是兩個(gè)函數(shù), 不用自己實(shí)現(xiàn)就可以使用, 如下:

const p = new Promise((resolve, reject) => {

})
console.log(p);

可以看到這個(gè)狀態(tài)默認(rèn)是Pending(進(jìn)行中)

resolve和reject這兩個(gè)參數(shù)(函數(shù))可以在處理器函數(shù)里面調(diào)用(可以傳遞參數(shù)), 這樣將會(huì)改變 Promise 對(duì)象的狀態(tài):

const p = new Promise((resolve, reject) => {
  resolve();
})
console.log(p);

當(dāng)調(diào)用resolve函數(shù)時(shí)會(huì)將Priomise對(duì)象的狀態(tài)修改為Fulfilled(成功), 調(diào)用reject函數(shù)則會(huì)將狀態(tài)修改為Rejected(失敗)

then方法

Promise實(shí)例的then方法, 可以接受兩個(gè)參數(shù)(都是函數(shù))分別指定Primise實(shí)例里面狀態(tài)(成功或失敗)改變時(shí)調(diào)用的回調(diào)函數(shù)(并且Promise的then方法是異步的微任務(wù)):

const p = new Promise((resolve, reject) => {
  // 將p的狀態(tài)修改為成功 
  resolve();
})
    
console.log("同步代碼");
p.then(
  () => {
    console.log("成功的回調(diào)");
  },
  () => {
    console.log("失敗的回調(diào)");
  }
)

結(jié)果如下:

反之調(diào)用reject函數(shù)就會(huì)觸發(fā)then方法的第二個(gè)回調(diào)函數(shù), 如果將resolve函數(shù)和reject函數(shù)都調(diào)用只會(huì)生效最先調(diào)用的(因?yàn)闋顟B(tài)時(shí)單向的嘛)

resolve 和 reject 的參數(shù)傳遞

經(jīng)過(guò)上面的測(cè)試我們知道resolve和reject這兩個(gè)函數(shù)是可以修改狀態(tài)并且觸發(fā)Promise的then方法的回調(diào)函數(shù)的, 那么是函數(shù)就可以傳遞參數(shù), 這個(gè)參數(shù)會(huì)被傳遞給對(duì)應(yīng)的then方法的回調(diào)函數(shù)接收到:

const p = new Promise((resolve, reject) => {
  // 將p的狀態(tài)修改為成功, 并且傳遞一個(gè)參數(shù)
  resolve("ok");
})

console.log("同步代碼");
p.then(
  // 這里接收 resolve 函數(shù)傳遞的參數(shù)
  res => {
    console.log("成功的回調(diào)", res);
  },
  // 這里接收 reject 函數(shù)傳遞的參數(shù)
  err => {
    console.log("失敗的回調(diào)");
  }
)

結(jié)果如下:

then()鏈?zhǔn)秸{(diào)用

了解完then方法以后我們就可以稍微修改一下一開(kāi)始最上面的需求:

// 請(qǐng)求函數(shù)使用 Promise 封裝
const baseUrl = "http://localhost:8888/test";
const request = (url) => {
  // 返回一個(gè)Promise實(shí)例
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', baseUrl + url);
    xhr.send();
    xhr.onreadystatechange = () => {
      const { readyState, status, response } = xhr;
      if (readyState === 4) {
        if (status >= 200 && status <= 299) {
          const res = JSON.parse(response);
          // 修改狀態(tài)為成功并且把響應(yīng)傳遞過(guò)去
          resolve(res);
        } else {
          // 修改狀態(tài)為失敗也把響應(yīng)傳遞過(guò)去
          reject(response);
        }
      }
    }
  })
}

// 使用then方法
request("/testData1").then(
  res1 => {
    console.log(res1); // {data: '用戶(hù)數(shù)據(jù)1'}
    request(`/testData2?data=${res1.data}`).then(
      res2 => {
        console.log(res2); // {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2'}
        request(`/testData3?data=${res2.data}`).then(
          res3 => {
            console.log("需求需要的數(shù)據(jù)", res3); // => 需求需要的數(shù)據(jù) {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2,用戶(hù)數(shù)據(jù)3'}
          }
        )
      }
    )
  }
)

寫(xiě)完以后發(fā)現(xiàn)好像還不如使用回調(diào)函數(shù)的方式寫(xiě), 看到這里好像Promise還是不能很好的解決回調(diào)嵌套的問(wèn)題; 換個(gè)思路如果我們?cè)趖hen方法中再返回一個(gè)Promise實(shí)例, 那么不就又可以調(diào)用then方法了嗎? 代碼中測(cè)試一下:

const p1 = new Promise((resolve, reject) => {
  resolve("p1數(shù)據(jù)");
})

// 這里的p2就是p1.then方法成功回調(diào)里面返回的p2
const p2 = p1.then(
    // 這里的res1參數(shù)就是p1的處理器函數(shù)中調(diào)用 resolve("p1數(shù)據(jù)") 傳遞的參數(shù)
    res1 => {
      console.log(res1); // p1數(shù)據(jù)

      // 這里新建一個(gè)新的Promise實(shí)例, 記作p2
      const p2 = new Promise((resolve, reject) => {
        // 0.5s后修改狀態(tài)
        setTimeout(() => {
          resolve(res1 + ",p2數(shù)據(jù)");
        }, 500);

      })
      // 將p2返回
      return p2;
    }
  )

const p3 = p2.then(
  res2 => {
    console.log(res2); // p1數(shù)據(jù),p2數(shù)據(jù)
    // 這里和上面的同理
    const p3 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(res2 + ",p3數(shù)據(jù)");
      }, 500);

    })
    return p3;
  }
)

p3.then(
  res3 => {
    console.log(res3); // p1數(shù)據(jù),p2數(shù)據(jù),p3數(shù)據(jù)
  }
)

發(fā)現(xiàn)可行后, 把需求代碼再修改一下:

// 這里的p1就是 request("/testData1") 返回的Promise實(shí)例
const p1 = request("/testData1");

// 這里的p2就是p1.then方法成功回調(diào)返回的p2
const p2 = p1.then(
    res1 => {
      console.log(res1); // {data: '用戶(hù)數(shù)據(jù)1'}

      // 再調(diào)用request方法將其返回的Promise實(shí)例記作p2
      const p2 = request(`/testData2?data=${res1.data}`);
      // 將p2返回
      return p2;
    }
  )

const p3 = p2.then(
  res2 => {
    console.log(res2); // {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2'}
    // 這里和上面的同理
    const p3 = request(`/testData3?data=${res2.data}`);
    return p3;
  }
)

p3.then(
  res3 => {
    console.log("需求需要的數(shù)據(jù)", res3); // 需求需要的數(shù)據(jù) {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2,用戶(hù)數(shù)據(jù)3'}
  }
)

需求實(shí)現(xiàn)是實(shí)現(xiàn)了, 就是代碼有點(diǎn)冗余, 可以精簡(jiǎn)一下, 如下:

request("/testData1").then(
  res1 => {
    console.log(res1); // {data: '用戶(hù)數(shù)據(jù)1'}
    // 這里直接返回 request方法的返回值Promise實(shí)例
    return request(`/testData2?data=${res1.data}`);
  }
).then( 
  // 這個(gè)成功回調(diào)時(shí)上一個(gè)then方法返回的Promise實(shí)例的成功回調(diào)
  res2 => {
    console.log(res2); // {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2'}
    // 同理
    return request(`/testData3?data=${res2.data}`);
  }
).then(
  // 同理
  res3 => {
    console.log("需求需要的數(shù)據(jù)", res3); // 需求需要的數(shù)據(jù) {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2,用戶(hù)數(shù)據(jù)3'}
  }
)

上面的代碼格式就像鏈條一樣所以又被稱(chēng)為"鏈?zhǔn)秸{(diào)用"

then()的返回值

經(jīng)過(guò)上面的代碼測(cè)試, then方法除了返回Promise對(duì)象外還可以返回其他任意的值, 返回會(huì)被轉(zhuǎn)換為Promise對(duì)象, 內(nèi)部的狀態(tài)視返回值而定:

const p1 = new Promise(resolve => resolve());
p1.then(() => {
  // 這里相當(dāng)于是 return undefined
}).then(
  res1 => {
    console.log(res1); // undefined
    return "hello";
  }
).then(
  res2 => {
    console.log(res2); // hello
    return { name: "張三" };
  }
).then(
  res3 => {
    console.log(res3); // { name: "張三" }

    // 返回錯(cuò)誤對(duì)象該P(yáng)romise對(duì)象的狀態(tài)也是成功
    return new Error("error object");
  }
).then(
  res4 => {
    console.log(res4 instanceof Error); // true
    console.log(res4.message); // error object

    // 拋出一個(gè)錯(cuò)誤則該P(yáng)romise對(duì)象的狀態(tài)是失敗
    throw "thorw error";
  }
).then(
  () => { },
  err => {
    console.log(err); // thorw error
  }
)

catch方法

上面使用then方法的鏈?zhǔn)秸{(diào)用可以解決回調(diào)嵌套太深的問(wèn)題, 但是還沒(méi)處理請(qǐng)求之間的失敗回調(diào)處理, then方法的第二個(gè)回調(diào)就是指定失敗的回調(diào), 但是一般都不使用這個(gè)回調(diào)來(lái)處理錯(cuò)誤, 而是使用catch方法來(lái)失敗, 使用格式如下:

p1.then(
  // ...
).then(
  // ...

).catch(err => { // 這個(gè)catch可以捕獲這一條調(diào)用鏈上的錯(cuò)誤
    // 處理錯(cuò)誤
})

finally方法

除了catch方法那自然就有finally方法, finally方法和try...catch...finally中的finally是一樣的作用, 使用格式如下:

p.then(
  // ...
).then(
  // ...

).catch(err => { // 這個(gè)catch方法可以捕獲這一條調(diào)用鏈上的錯(cuò)誤
  // 處理錯(cuò)誤
    
}).finally(() => { // 無(wú)論成功還是失敗這個(gè)finally中指定的回調(diào)都會(huì)執(zhí)行
  // 清除loading, 重置狀態(tài)...
})

Promise的方法

Promise.resolve()

立即返回一個(gè)狀態(tài)是成功(Fulfilled) 的 Promise 對(duì)象, 可以傳遞參數(shù), 參數(shù)會(huì)被其返回的Promise實(shí)例的then方法的回調(diào)(異步微任務(wù))接受到:

const p = Promise.resolve("Promise.resolve");
p.then(res => console.log(res)); // Promise.resolve

也可以利用Promise.resolve()來(lái)創(chuàng)建一個(gè)微任務(wù):

console.log("同步代碼");

setTimeout(() => console.log("setTimeout"), 0);

const p = Promise.resolve("Promise.resolve");
p.then(res => console.log(res));

結(jié)果如下:

Promise.reject()

Promise.resolve()一樣不過(guò)返回的狀態(tài)是失敗(Rejected) 的Promise對(duì)象(同樣是微任務(wù))

console.log("同步代碼");

setTimeout(() => console.log("setTimeout"), 0);

const p = Promise.reject("Promise.reject");
p.then().catch(err => console.log("Promise.reject"));

Promise.all()

Promise.all接收一個(gè)Promise的iterable類(lèi)型(就是可迭代對(duì)象里面存放著Promise實(shí)例, Array, Map, Set都屬于ES6的iterable類(lèi)型), Promise.all 會(huì)等待所有的Promise對(duì)象都完成(或第一個(gè)失敗) , 根據(jù)給定的參數(shù)返回不同的參數(shù)

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("p1 data"), 500);
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("p2 data"), 1000);
})
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("p3 data"), 1500);
})

// 依次打印數(shù)據(jù)
p1.then(res => console.log(res)); // 0.5s 后打印 p1 data
p2.then(res => console.log(res)); // 1.0s 后打印 p2 data
p3.then(res => console.log(res)); // 1.5s 后打印 p3 data


const proArray = [p1, p2, p3];
Promise.all(proArray).then(resList => {
  console.log(resList); // 1.5s后打印數(shù)據(jù) ['p1 data', 'p2 data', 'p3 data']
}).catch(err => {
  // 如果在`Promise.all`方法中出現(xiàn)了失敗的狀態(tài), 那么這個(gè)參數(shù)會(huì)是這個(gè)失敗狀態(tài)返回的參數(shù)(如果有的話)
  console.error("error: ", err); 
})

利用Promise.all()方法的特定可以用于同時(shí)發(fā)送多個(gè)請(qǐng)求, 如下例子:

Node接口:

const getRandom = () => Math.random() * 9 + 1;
router.get('/testData4', (req, res) => {
    let n = req.query.n;
    const random = getRandom();

    // 定時(shí)器模擬接口響應(yīng)時(shí)間差
    setTimeout(() => {
        res.json(`第${++n}個(gè)請(qǐng)求${random}`);
    }, random * 50);
});

前端發(fā)送多個(gè)請(qǐng)求:

const proArray = [];
for (let i = 0; i < 10; i++) {
  // 將10個(gè)請(qǐng)求方法返回的Promsie對(duì)象添加到proArray數(shù)組中
  proArray.push(request(`/testData4?n=${i}`));
}
Promise.all(proArray).then(resList => {
  for (const res of resList) {
    // 每個(gè)請(qǐng)求的返回結(jié)果
    console.log(res);
  }
})

Promise.allSettled()

Promise.allSettled()方法和Promise.all()很類(lèi)似只不過(guò)是接受的期約對(duì)象無(wú)論是成功還是失敗都會(huì)觸發(fā)then方法的成功回調(diào), 每個(gè)期約都會(huì)返回一個(gè)對(duì)象status屬性表示狀態(tài), value表示成功回調(diào)的值, 如果狀態(tài)是失敗的那么失敗回調(diào)的值存儲(chǔ)在reason屬性中:

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("p1 data"), 500);;
})
const p2 = new Promise((resolve, reject) => {
  // p2的Promise實(shí)例的狀態(tài)修改為失敗
  setTimeout(() => reject("p2 err"), 1000);
})

const proArray = [p1, p2];
Promise.allSettled(proArray).then(resList => {
  console.log(resList); // (2) [{…}, {…}]

  for (const res of resList) {
    const { status, value, reason } = res;
    if (reason) {
      console.log(`失敗: ${status}, 原因是: ${reason}`); // 失敗: rejected, 原因是: p2 err
    } else { 
      console.log(`成功: ${status}, 數(shù)據(jù)是: ${value}`); // 成功: fulfilled, 數(shù)據(jù)是: p1 data
    }
  }
}).catch(err => {
  // 這里不會(huì)捕獲p2的失敗的回調(diào) 
  console.error("error: ", err);
})

Promise.race()

Promise.race()Promise.all()類(lèi)似, 都接收一個(gè)可以迭代的參數(shù), 但是不同之處是

Promise.race()的狀態(tài)變化不是受全部參數(shù)的狀態(tài)影響, 一旦迭代器中的某個(gè)Promise解決或拒絕,返回的 Promise就會(huì)解決或拒絕

const p1 = new Promise((resolve, reject) => {
    setTimeout(() => resolve("p1 data"), 500);;
})
const p2 = new Promise((resolve, reject) => {
    setTimeout(() => reject("p2 err"), 1000);
})

const proArray = [p1, p2];
Promise.race(proArray).then(res => {
    console.log(res); // p1 data
}).catch(err => {
    console.error(err);
})

async 和 await

async函數(shù)函數(shù)

async函數(shù)函數(shù)就是使用async關(guān)鍵字聲明的函數(shù)(也叫異步函數(shù)), async函數(shù)和普通的函數(shù)使用沒(méi)有什么區(qū)別:

// 函數(shù)聲明
async function asyncFn1() {
  console.log("asyncFn1");
}

// 函數(shù)表達(dá)式
const asyncFn2 = async () => {
  console.log("asyncFn2");
}

asyncFn1();
asyncFn2();

// 立即調(diào)用
(async () => {
  console.log("asyncFn3");
})();

await

await操作符用于等待一個(gè)Promise對(duì)象, 它只能在async function中使用, 使用async+await可以將異步的代碼"變"的跟同步的一樣:

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("data");
  }, 1000);
});

// async 函數(shù)
async function asyncFn() {
  console.log("asyncFn函數(shù)開(kāi)始執(zhí)行");

  // await 會(huì)等待右邊的Promise對(duì)象的狀態(tài)變成功后返回其值
  const res = await p;
  console.log(res); // data

  console.log("asyncFn函數(shù)執(zhí)行完了");
}

// 調(diào)用
asyncFn();

上面的代碼會(huì)先輸出"asyncFn函數(shù)開(kāi)始執(zhí)行"后, 等待1s左右輸出"data", 然后再輸出"asyncFn函數(shù)執(zhí)行完了"

注意: 異步函數(shù)不會(huì)阻塞主線程的執(zhí)行, 它是異步的:

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("data");
  }, 1000);
});

// async 函數(shù)
async function asyncFn() {
  console.log("asyncFn函數(shù)開(kāi)始執(zhí)行");
  const res = await p;
  console.log(res); 
  console.log("asyncFn函數(shù)執(zhí)行完了");
}

console.log("hello");
asyncFn(); 
console.log("javascript");

等待大約1s后上面的代碼執(zhí)行結(jié)果如下:

根據(jù)上面的代碼執(zhí)行結(jié)果, 我們發(fā)現(xiàn)異步函數(shù)內(nèi)await關(guān)鍵字會(huì)等待其右邊的Promise對(duì)象的返回值, 等待結(jié)束后, 后面的代碼才會(huì)被執(zhí)行, 利用這個(gè)特性我們可以在JavaScript中可以實(shí)現(xiàn)類(lèi)似Java的Thread.sleep()方法, 如下:

const sleep = async time => new Promise(resolve => setTimeout(resolve, time));

(async () => {
  console.log("1");
  await sleep(1000);
  console.log("2");
  await sleep(500);
  console.log("2.5");
})();

了解完async和await的使用后我們可以使用異步函數(shù)再來(lái)完成我們一開(kāi)始的需求:

// 請(qǐng)求函數(shù)使用 Promise 封裝
const baseUrl = "http://localhost:8888/test";
const request = (url) => {
  // 返回一個(gè)Promise實(shí)例
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', baseUrl + url);
    xhr.send();
    xhr.onreadystatechange = () => {
      const { readyState, status, response } = xhr;
      if (readyState === 4) {
        if (status >= 200 && status <= 299) {
          const res = JSON.parse(response);
          // 修改狀態(tài)為成功并且把響應(yīng)傳遞過(guò)去
          resolve(res);
        } else {
          // 修改狀態(tài)為失敗也把響應(yīng)傳遞過(guò)去
          reject(response);
        }
      }
    }
  })
}

async function asyncFn() {
  const res1 = await request("/testData1");
  console.log(res1); // {data: '用戶(hù)數(shù)據(jù)1'}

  const res2 = await request(`/testData2?data=${res1.data}`);
  console.log(res2); // {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2'}

  const res3 = await request(`/testData3?data=${res2.data}`);
  console.log(res3); // {data: '用戶(hù)數(shù)據(jù)1,用戶(hù)數(shù)據(jù)2,用戶(hù)數(shù)據(jù)3'}
}

asyncFn();

可以看到使用async和await來(lái)發(fā)送網(wǎng)絡(luò)請(qǐng)求寫(xiě)的代碼很簡(jiǎn)潔, 也很直觀

異步函數(shù)的錯(cuò)誤處理

異步函數(shù)的異常處理可以使用try...catch和catch處理:

try...catch

async function asyncFn() {
  let res1, res2, res3;
  try {
    res1 = await request("/testData1");

    try {
      if (res1?.data) {
        res2 = await request(`/testData2?data=${res1.data}`);
      }

      try {
        if (res2?.data) {
          res3 = await request(`/testData3?data=${res2.data}`);
        }

      } catch (error) {
        // 處理res3 error
      }

    } catch (error) {
      // 處理res2 error
    }

  } catch (error) {
    // 處理res3 error
  }
}

catch

async function asyncFn() {
  const res1 = await request("/testData1")
    .catch(err => {
      // 處理res1 error
    });

  if (res1?.data) {
    const res2 = await request(`/testData2?data=${res1.data}`)
      .catch(err => {
        // 處理res2 error
      });

    if (res2?.data) {
      const res3 = await request(`/testData3?data=${res2.data}`)
        .catch(err => {
          // 處理res3 error
        });
    }
  }
}

異步函數(shù)同樣適用于Promise的一些靜態(tài)方法

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("p1 data"), 500);
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("p2 data"), 1000);
})
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("p3 data"), 1500);
})

const proArray = [p1, p2, p3];
async function asyncFn() {
  const list = await Promise.all(proArray);
  console.log(list); // ['p1 data', 'p2 data', 'p3 data']
}
asyncFn();

for await...of

一個(gè)數(shù)組中存儲(chǔ)多個(gè)Promise對(duì)象我們可以通過(guò)Promise.all獲取或者通過(guò)循環(huán)來(lái)等待其返回值, 我們使用循環(huán):

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("p1 data"), 500);
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("p2 data"), 1000);
})
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("p3 data"), 1500);
})

const proArray = [p1, p2, p3];

async function asyncFn() {
  for (const pro of proArray) {
    // 這里不要忘記 await
    const res = await pro;
    console.log(res);
  }
}
asyncFn();

ES9開(kāi)始有一個(gè)新語(yǔ)法就是for await..of可以自動(dòng)等待每次循環(huán)的項(xiàng)

async function asyncFn() {
  for await (const item of proArray) {
    console.log(item);
  }
}
asyncFn();

參考:

總結(jié)

到此這篇關(guān)于JavaScript中Promise使用的文章就介紹到這了,更多相關(guān)JS Promise的使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JS獲取url參數(shù),JS發(fā)送json格式的POST請(qǐng)求方法

    JS獲取url參數(shù),JS發(fā)送json格式的POST請(qǐng)求方法

    下面小編就為大家分享一篇JS獲取url參數(shù),JS發(fā)送json格式的POST請(qǐng)求方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • js實(shí)現(xiàn)股票實(shí)時(shí)刷新數(shù)據(jù)案例

    js實(shí)現(xiàn)股票實(shí)時(shí)刷新數(shù)據(jù)案例

    下面小編就為大家?guī)?lái)一篇js實(shí)現(xiàn)股票實(shí)時(shí)刷新數(shù)據(jù)案例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05
  • js實(shí)現(xiàn)貪吃蛇游戲含注釋

    js實(shí)現(xiàn)貪吃蛇游戲含注釋

    這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)貪吃蛇游戲含注釋?zhuān)闹惺纠a介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-03-03
  • 利用百度echarts實(shí)現(xiàn)圖表功能簡(jiǎn)單入門(mén)示例【附源碼下載】

    利用百度echarts實(shí)現(xiàn)圖表功能簡(jiǎn)單入門(mén)示例【附源碼下載】

    這篇文章主要介紹了利用百度echarts實(shí)現(xiàn)圖表功能簡(jiǎn)單,結(jié)合簡(jiǎn)單示例形式分析了echarts插件的圖標(biāo)繪制功能相關(guān)實(shí)現(xiàn)技巧,并附帶源碼供讀者下載參考,需要的朋友可以參考下
    2019-06-06
  • 第六篇Bootstrap表格樣式介紹

    第六篇Bootstrap表格樣式介紹

    這篇文章主要介紹了第六篇Bootstrap表格樣式介紹的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-06-06
  • 微信JS接口大全

    微信JS接口大全

    這篇文章主要為大家分享了最全面詳細(xì)的微信JS接口大全,希望對(duì)大家有幫助,感興趣的小伙伴們可以參考一下
    2016-08-08
  • javascript框架設(shè)計(jì)之類(lèi)工廠

    javascript框架設(shè)計(jì)之類(lèi)工廠

    這篇文章主要介紹了javascript框架設(shè)計(jì)之類(lèi)工廠的相關(guān)資料,非常淺顯易懂,有需要的小伙伴可以查看下。
    2015-06-06
  • JS實(shí)現(xiàn)將對(duì)象轉(zhuǎn)化為數(shù)組的方法分析

    JS實(shí)現(xiàn)將對(duì)象轉(zhuǎn)化為數(shù)組的方法分析

    這篇文章主要介紹了JS實(shí)現(xiàn)將對(duì)象轉(zhuǎn)化為數(shù)組的方法,結(jié)合實(shí)例形式分析了javascript操作及轉(zhuǎn)換json數(shù)組相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2019-01-01
  • 微信小程序input抖動(dòng)問(wèn)題的修復(fù)方法

    微信小程序input抖動(dòng)問(wèn)題的修復(fù)方法

    這篇文章主要給大家介紹了關(guān)于微信小程序input抖動(dòng)問(wèn)題的修復(fù)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • JSON字符串和對(duì)象相互轉(zhuǎn)換實(shí)例分析

    JSON字符串和對(duì)象相互轉(zhuǎn)換實(shí)例分析

    這篇文章主要介紹了JSON字符串和對(duì)象相互轉(zhuǎn)換的方法,結(jié)合實(shí)例形式分析了json格式數(shù)據(jù)的轉(zhuǎn)換方法,涉及javascript正則與字符串操作的相關(guān)技巧,需要的朋友可以參考下
    2016-06-06

最新評(píng)論