成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

Promise 的鏈?zhǔn)秸{(diào)用與中止

cuieney / 3765人閱讀

摘要:一是如何鏈?zhǔn)秸{(diào)用,二是如何中止鏈?zhǔn)秸{(diào)用。到目前為止,我們就基本了解了的用法及特點(diǎn),并實(shí)現(xiàn)用重構(gòu)用回調(diào)函數(shù)寫的異步操作。

Abstract

本文主要講的是如何實(shí)現(xiàn) Promise 的鏈?zhǔn)秸{(diào)用。也就是 promise().then().then().catch() 的形式,然后討論如何在某一個(gè) then() 里面中止 Promise。

在程序中,只要返回了一個(gè) promise 對(duì)象,如果 promise 對(duì)象不是 Rejected 或 Fulfilled 狀態(tài),then 方法就會(huì)繼續(xù)調(diào)用。利用這個(gè)特性,可以處理多個(gè)異步邏輯。但有時(shí)候某個(gè) then 方法的執(zhí)行結(jié)果可能會(huì)決定是否需要執(zhí)行下一個(gè) then,這個(gè)時(shí)候就需中止 promise,主要思想就是使用 reject 來中止 promise 的 then 繼續(xù)執(zhí)行。

“中止”這個(gè)詞不知道用得是否準(zhǔn)確。這里可能還是 break 的含義更精確,跳出本次 promise,不繼續(xù)執(zhí)行后面的 then 方法。但 promise 依舊會(huì)繼續(xù)執(zhí)行。

Can I use promises

當(dāng)前瀏覽器對(duì) Promise 的支持情況見下圖:

http://caniuse.com/#search=promise

Promise

先簡單復(fù)習(xí)一下 Promise。Promise 其實(shí)很簡單,就是一個(gè)處理異步的方法。一般可以通過 new 方法來調(diào)用 Promise 的構(gòu)造器實(shí)例化一個(gè) promise 對(duì)象:

var promise = new Promise((resolve, reject) => {
    // 異步處理
    // 處理結(jié)束后,調(diào)用 resolve 或 reject
    //      成功時(shí)就調(diào)用 resolve
    //      失敗時(shí)就調(diào)用 reject
});

new Promise 實(shí)例化的 promise 對(duì)象有以下三個(gè)狀態(tài):

"has-resolution" - Fulfilled。resolve(成功)時(shí),此時(shí)會(huì)調(diào)用 onFulfilled

"has-rejection" - Rejected。reject(失敗)時(shí),此時(shí)會(huì)調(diào)用 onRejected

"unresolved" - Pending。既不是resolve也不是reject的狀態(tài),也就是promise對(duì)象剛被創(chuàng)建后的初始化狀態(tài)等

關(guān)于上面這三種狀態(tài)的讀法,其中左側(cè)為在 ES6 Promises 規(guī)范中定義的術(shù)語, 而右側(cè)則是在 Promises/A+ 中描述狀態(tài)的術(shù)語。基本上狀態(tài)在代碼中是不會(huì)涉及到的,所以名稱也無需太在意。

Promise Chain

先來假設(shè)一個(gè)業(yè)務(wù)需求:在系統(tǒng)中使用教務(wù)系統(tǒng)賬號(hào)進(jìn)行登錄。首先用戶在登錄頁面輸入用戶名(教務(wù)系統(tǒng)賬號(hào))和密碼(教務(wù)系統(tǒng)密碼);然后判斷數(shù)據(jù)庫中是否存在該用戶;如果不存在則使用用戶名和密碼模擬登錄教務(wù)系統(tǒng),如果模擬登錄成功,則存儲(chǔ)用戶名和密碼,并返回登錄成功。

聽起來就有點(diǎn)復(fù)雜對(duì)不對(duì)?于是畫了個(gè)流程圖來解釋整個(gè)業(yè)務(wù)邏輯:

上圖只是一個(gè)簡化版本,比如密碼加密、session設(shè)置等沒有表現(xiàn)出來,大家知道就好。圖中 (1)(2)、(3) 三個(gè)地方就是會(huì)進(jìn)行異步處理的地方,一般數(shù)據(jù)庫操作、網(wǎng)絡(luò)請(qǐng)求都是異步的。

如果用傳統(tǒng)的回調(diào)函數(shù) callback 來處理上面的邏輯,嵌套的層級(jí)就會(huì)比較深,上面的業(yè)務(wù)因?yàn)橛腥齻€(gè)異步操作所以有三層回調(diào),代碼大概會(huì)是下面的樣子:

// 根據(jù) name 查詢用戶信息
findUserByName(name, function(err, userinfo) {
  if (err) {
    return res.json({
      code: 1000,
      message: "查詢用戶信息,數(shù)據(jù)庫操作數(shù)出現(xiàn)異常",
    });
  }


  if (userinfo.length > 0) {
  // 用戶存在
  if (userinfo[0].pwd === pwd)
    // 密碼正確
    return res.json({
      code: 0,
      message: "登錄成功",
    });
  }

  // 數(shù)據(jù)庫中不存在該用戶,模擬登錄教務(wù)系統(tǒng)
  loginEducationSystem(name, pwd, function(err, result) {
    if (err) {
      return res.json({
        code: 1001,
        message: "模擬登錄教務(wù)系統(tǒng)出現(xiàn)異常",
      });
    }

    // 約定正確情況下,code 為 0
    if (result.code !== 0) {
      return res.json({
        code: 1002,
        message: "模擬登錄教務(wù)系統(tǒng)失敗,可能是用戶名或密碼錯(cuò)誤",
      });
    }

    // 模擬登錄成功,將用戶名密碼存入數(shù)據(jù)庫
    saveUserToDB(name, pwd, function(err, result) {
      if (err) {
        return res.json({
          code: 1003,
          message: "將用戶名密碼存入數(shù)據(jù)庫出現(xiàn)異常",
        });
      }
      if (result.code !== 0) {
        return res.json({
          code: 1004,
          message: "將用戶名密碼存入數(shù)據(jù)庫出現(xiàn)異常",
        });
      }

      return res.json({
        code: 0,
        message: "登錄成功!",
      });
    });
  });
});

上面的代碼可能存在的不優(yōu)雅之處:

隨著業(yè)務(wù)邏輯變負(fù)責(zé),回調(diào)層級(jí)會(huì)越來越深

代碼耦合度比較高,不易修改

每一步操作都需要手動(dòng)進(jìn)行異常處理,比較麻煩

接下來再用 promise 實(shí)現(xiàn)此處的業(yè)務(wù)需求。使用 promise 編碼之前,可以先思考兩個(gè)問題。

一是如何鏈?zhǔn)秸{(diào)用,二是如何中止鏈?zhǔn)秸{(diào)用。

How to Use Promise Chain

業(yè)務(wù)中有三個(gè)需要異步處理的功能,所以會(huì)分別實(shí)例化三個(gè) promise 對(duì)象,然后對(duì) promise 進(jìn)行鏈?zhǔn)秸{(diào)用。那么,如何進(jìn)行鏈?zhǔn)秸{(diào)用?

其實(shí)也很簡單,直接在 promise 的 then 方法里面返回另一個(gè) promise 即可。例如:

function start() {
  return new Promise((resolve, reject) => {
    resolve("start");
  });
}

start()
  .then(data => {
    // promise start
    console.log("result of start: ", data);
    return Promise.resolve(1); // p1
  })
  .then(data => {
    // promise p1
    console.log("result of p1: ", data);
    return Promise.reject(2); // p2
  })
  .then(data => {
    // promise p2
    console.log("result of p2: ", data);
    return Promise.resolve(3); // p3
  })
  .catch(ex => {
    // promise p3
    console.log("ex: ", ex);
    return Promise.resolve(4); // p4
  })
  .then(data => {
    // promise p4
    console.log("result of p4: ", data);
  });

上面的代碼最終會(huì)輸出:

result of start:  start
result of p1:  1
ex:  2
result of p4:  4

代碼的執(zhí)行邏輯如圖:

從圖中可以看出來,代碼的執(zhí)行邏輯是 promise start --> promise p1 --> promise p3 --> promise p4。所以結(jié)合輸出結(jié)果和執(zhí)行邏輯圖,總結(jié)出以下幾點(diǎn):

promise 的 then 方法里面可以繼續(xù)返回一個(gè)新的 promise 對(duì)象

下一個(gè) then 方法的參數(shù)是上一個(gè) promise 對(duì)象的 resolve 參數(shù)

catch 方法的參數(shù)是其之前某個(gè) promise 對(duì)象的 rejecte 參數(shù)

一旦某個(gè) then 方法里面的 promise 狀態(tài)改變?yōu)榱?rejected,則promise 方法連會(huì)跳過后面的 then 直接執(zhí)行 catch

catch 方法里面依舊可以返回一個(gè)新的 promise 對(duì)象

How to Break Promise Chain

接下來就該討論如何中止 promise 方法鏈了。

通過上面的例子,我們可以知道 promise 的狀態(tài)改變?yōu)?rejected 后,promise 就會(huì)跳過后面的 then 方法。

也就是,某個(gè) then 里面發(fā)生異常后,就會(huì)跳過 then 方法,直接執(zhí)行 catch。

所以,當(dāng)在構(gòu)造的 promise 方法鏈中,如果在某個(gè) then 后面,不需要再執(zhí)行 then 方法了,就可以把它當(dāng)作一個(gè)異常來處理,返回一個(gè)異常信息給 catch,其參數(shù)可自定義,比如該異常的參數(shù)信息為 { notRealPromiseException: true},然后在 catch 里面判斷一下 notRealPromiseException 是否為 true,如果為 true,就說明不是程序出現(xiàn)異常,而是在正常邏輯里面中止 then 方法的執(zhí)行。

代碼大概就這樣:

start()
  .then(data => {
    // promise start
    console.log("result of start: ", data);
    return Promise.resolve(1); // p1
    )
  .then(data => {
    // promise p1
    console.log("result of p1: ", data);
    return Promise.reject({
      notRealPromiseException: true,
    }); // p2
  })
  .then(data => {
    // promise p2
    console.log("result of p2: ", data);
    return Promise.resolve(3); // p3
  })
  .catch(ex => {
    console.log("ex: ", ex);
    if (ex.notRealPromiseException) {
      // 一切正常,只是通過 catch 方法來中止 promise chain
      // 也就是中止 promise p2 的執(zhí)行
      return true;
    }
    // 真正發(fā)生異常
    return false;
  });

這樣的做法可能不符合 catch 的語義。不過從某種意義上來說,promise 方法鏈沒有繼續(xù)執(zhí)行,也可以算是一種“異?!?。

Refactor Callback with Promise

講了那么多道理,現(xiàn)在就改來使用 promise 重構(gòu)之前用回調(diào)函數(shù)寫的異步邏輯了。

// 據(jù) name 查詢用戶信息
const findUserByName = (name, pwd) => {
  return new Promise((resolve, reject) => {
    // 數(shù)據(jù)庫查詢操作
    if (dbError) {
      // 數(shù)據(jù)庫查詢出錯(cuò),將 promise 設(shè)置為 rejected
      reject({
        code: 1000,
        message: "查詢用戶信息,數(shù)據(jù)庫操作數(shù)出現(xiàn)異常",
      });
    }
    // 將查詢結(jié)果賦給 userinfo 變量
    if (userinfo.length === 0) {
      // 數(shù)據(jù)庫中不存在該用戶
      resolve();
    }
    // 數(shù)據(jù)庫存在該用戶,判斷密碼是否正確
    if (pwd === userinfo[0].pwd) {
      // 密碼正確,中止 promise 執(zhí)行
      reject({
        notRealPromiseException: true,
        data: {
          code: 0,
          message: "密碼正確,登錄成功",
        }
      });
    }
    // 密碼不正確,登錄失敗,將 Promise 設(shè)置為 Rejected 狀態(tài)
    reject({
      code: 1001,
      message: "密碼不正確,登錄失敗",
    });
  });
};


// 模擬登錄教務(wù)系統(tǒng)
const loginEducationSystem = (name, pwd) => {
  // 登錄邏輯...
  // 登錄成功
  resolve();
  // 登錄失敗
  reject({
    code: 1002,
    message: "模擬登錄教務(wù)系統(tǒng)失敗",
  });
};


// 將用戶名密碼存入數(shù)據(jù)庫
const saveUserToDB(name, pwd) => {
  // 數(shù)據(jù)庫存儲(chǔ)操作
  if (dbError) {
    // 數(shù)據(jù)庫存儲(chǔ)出錯(cuò),將 promise 設(shè)置為 rejected
    reject({
      code: 1004,
      message: "數(shù)據(jù)庫存儲(chǔ)出錯(cuò),將出現(xiàn)異常",
    });
  }
  // 數(shù)據(jù)庫存儲(chǔ)操作成功
  resolve();
};


findUserByName(name)
.then(() => {
  return loginEducationSystem(name, pwd);
})
.then(() => {
  return saveUserToDB(name, pwd);
})
.catch(e => {
  // 判斷異常出現(xiàn)原因
  if (e.notRealPromiseException) {
    // 正常中止 promise 而故意設(shè)置的異常
    return res.json(e.data);
  }
  // 出現(xiàn)錯(cuò)誤或異常
  return res.json(e);
});

在上面的代碼中,實(shí)例化了三個(gè) promise 對(duì)象,分別實(shí)現(xiàn)業(yè)務(wù)需求中的三個(gè)功能。然后通過 promise 方法鏈來調(diào)用。相比用回調(diào)函數(shù)而言,代碼結(jié)構(gòu)更加清晰,也更易讀易懂耦合度更低更易擴(kuò)展了。

Promise.all && Promise.race

仔細(xì)觀察可以發(fā)現(xiàn),在上面的 promise 代碼中,loginEducationSystemsaveUserToDB 兩個(gè)方法執(zhí)行有先后順序要求,但沒有數(shù)據(jù)傳遞。

其實(shí) promise 方法鏈更好用的一點(diǎn)是,當(dāng)下一個(gè)操作依賴于上一個(gè)操作的結(jié)果的時(shí)候,可以很方便地通過 then 方法的參數(shù)來傳遞數(shù)據(jù)。前面頁提到過,下一個(gè) then 方法的參數(shù)就是上一個(gè) then 方法里面 resolve 的參數(shù),所以當(dāng)然就可以把上一個(gè) then 方法的執(zhí)行結(jié)果作為參數(shù)傳遞給下一個(gè) then 方法了。

還有些時(shí)候,可能 then 方法的執(zhí)行順序也沒有太多要求,只需要 promise 方法鏈中的兩個(gè)或多個(gè) promise 全部都執(zhí)行正確。這時(shí),如果依舊一個(gè)一個(gè)去寫 then 可能就比較麻煩,比如:

function p1() {
  return new Promise((resolve) => {
    console.log(1);
    resolve();
  });
}

function p2() {
  return new Promise((resolve) => {
    console.log(2);
    resolve();
  });
}

function p3() {
  return new Promise((resolve) => {
    console.log(3);
    resolve();
  });
}

現(xiàn)在只需要 p1 p2 p3 這三個(gè) promise 都執(zhí)行,并且 promise 最終狀態(tài)都是 Fulfilled,那么如果還是使用方法鏈,這是這樣調(diào)用:

p1()
.then(() => {
  return p2();
})
.then(() => {
  return p3();
})
.then(() => {
  console.log("all done");
})
.catch(e => {
  console.log("e: ", e);
});

// 輸出結(jié)果:
// 1
// 2
// 3
// all done

代碼貌似就不那么精煉了。這個(gè)時(shí)候就有了 Promise.all 這個(gè)方法。

Promise.all 接收一個(gè) promise對(duì)象的數(shù)組作為參數(shù),當(dāng)這個(gè)數(shù)組里的所有 promise 對(duì)象全部變?yōu)?resolve 或 reject 狀態(tài)的時(shí)候,它才會(huì)去調(diào)用 then 方法。

于是,調(diào)用這幾個(gè) promise 的代碼就可以這樣寫了:

p1()
.then(() => {
  return Promise.all([
    p2(),
    p3(),
  ]);
})
.then(() => {
  console.log("all done");
})
.catch((e) => {
  console.log("e: ", e);
});

// 輸出結(jié)果:
// 1
// 2
// 3
// all done

這樣看起來貌似就精煉些了。

而對(duì)于 Promise.race,其參數(shù)也跟 Promise.all 一樣是一個(gè)數(shù)組。只是數(shù)組中的任何一個(gè) promise 對(duì)象如果變?yōu)?resolve 或者reject 的話,該函數(shù)就會(huì)返回,并使用這個(gè) promise 對(duì)象的值進(jìn)行 resolve 或者 reject。

這里就不舉例了。

Conclusion

到目前為止,我們就基本了解了 Promise 的用法及特點(diǎn),并實(shí)現(xiàn)用 Promise 重構(gòu)用回調(diào)函數(shù)寫的異步操作?,F(xiàn)在對(duì) Promise 的使用,應(yīng)該駕輕就熟了。

完。

Github Issue: https://github.com/nodejh/nodejh.github.io/issues/23

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/81048.html

相關(guān)文章

  • Promise 對(duì)象理解

    摘要:使用對(duì)象的好處在于可以將異步操作以同步操作的流程表達(dá)出來,避免了層層嵌套的回調(diào)函數(shù)。對(duì)象異步操作拋出錯(cuò)誤,狀態(tài)就會(huì)變?yōu)?,就?huì)調(diào)用方法指定的回調(diào)函數(shù)處理這個(gè)錯(cuò)誤。 Promise 含義 Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大。它由社區(qū)最早提出和實(shí)現(xiàn),ES6 將其寫進(jìn)了語言標(biāo)準(zhǔn),統(tǒng)一了用法,原生提供了 Promise 對(duì)象。 所謂 P...

    church 評(píng)論0 收藏0
  • 異步Promise及Async/Await可能最完整入門攻略

    摘要:的和我們通過的原型方法拿到我們的返回值輸出我延遲了毫秒后輸出的輸出下列的值我延遲了毫秒后輸出的。有人說,我不想耦合性這么高,想先執(zhí)行函數(shù)再執(zhí)行,但不想用上面那種寫法,可以嗎,答案是當(dāng)然可以。 此文只介紹Async/Await與Promise基礎(chǔ)知識(shí)與實(shí)際用到注意的問題,將通過很多代碼實(shí)例進(jìn)行說明,兩個(gè)實(shí)例代碼是setDelay和setDelaySecond。 tips:本文系原創(chuàng)轉(zhuǎn)自...

    lingdududu 評(píng)論0 收藏0
  • indexedDB事務(wù)功能Promise化封裝

    摘要:綜上,對(duì)進(jìn)行一定的封裝,來簡化編碼操作?;膰L試對(duì)于這種帶大量回調(diào)的,使用進(jìn)行異步化封裝是個(gè)好主意。因此包括在內(nèi)的所有異步方法都會(huì)強(qiáng)制中止當(dāng)前事務(wù)。這就決定了一個(gè)事務(wù)內(nèi)部的所有操作必須是同步完成的。目前只實(shí)現(xiàn)了和,其他的有待下一步工作。 前言 本文是介紹我在編寫indexedDB封裝庫中誕生的一個(gè)副產(chǎn)品——如何讓indexedDB在支持鏈?zhǔn)秸{(diào)用的同時(shí),保持對(duì)事務(wù)的支持。項(xiàng)目地址:htt...

    zombieda 評(píng)論0 收藏0
  • 一起來學(xué)Promise

    摘要:參數(shù)如前面所提到的,方法只是方法的一個(gè)語法糖,原因就在于方法的參數(shù)為實(shí)際上是兩個(gè)回調(diào)函數(shù),分別用于處理調(diào)用它的對(duì)象的和狀態(tài),而方法就等價(jià)于狀態(tài)處理函數(shù)。對(duì)象狀態(tài)傳遞和改變的方法利用回調(diào)的返回值,可以控制某個(gè)操作后方法返回的對(duì)象及其狀態(tài)。 注意,本文主要針對(duì)ES6標(biāo)準(zhǔn)實(shí)現(xiàn)的Promise語法進(jìn)行闡述,實(shí)例代碼也都使用ES6語法,快速入門ES6請(qǐng)參見ECMAScript 6 掃盲。 一分鐘...

    liaoyg8023 評(píng)論0 收藏0
  • 深入學(xué)習(xí)Promise調(diào)用

    摘要:前言使用中,鏈?zhǔn)降恼{(diào)用對(duì)于控制異步執(zhí)行很重要。的鏈?zhǔn)秸{(diào)用是支持鏈?zhǔn)秸{(diào)用的,但是它是不同于上面的鏈?zhǔn)?。是調(diào)用方法返回自身,但是是調(diào)用方法后返回一個(gè)新的。的運(yùn)行機(jī)制請(qǐng)參考的運(yùn)行機(jī)制值穿透由于通過沒有成功添加回調(diào)函數(shù),發(fā)生了值穿透。 前言 使用Promise中,鏈?zhǔn)降恼{(diào)用對(duì)于控制異步執(zhí)行很重要。 鏈?zhǔn)秸{(diào)用 在jQuery的使用中,我們常常使用下面的代碼 $(#app).show().css(...

    tianren124 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<