中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

深入 Promise(三)——命名 Promise

發布時間:2020-07-05 12:04:11 來源:網絡 閱讀:450 作者:石墨文檔 欄目:開發技術

我們經常會遇到這種情況:比如通過用戶名查找并返回該用戶信息和他的關注者。通常有兩種方法:

  1. 定義一個外部變量:

    var usergetUserByName('nswbmw')
      .then((_user) => {
        user = _user
        return getFollowersByUserId(user._id)
      })
      .then((followers) => {
        return {
          user,
          followers
        }
      })
  2. 使用閉包:

    getUserByName('nswbmw')
      .then((user) => {
        return getFollowersByUserId(user._id).then((followers) => {
          return {
            user,
            followers
          }
        })
      })

兩種實現都可以,但都不太美觀。于是我之前產生了一個想法:同一層的 then 的參數是之前所有 then 結果的逆序。體現在代碼上就是:

Promise.resolve()
  .then(function () {
    return getUserByName('nswbmw')
  })
  .then(function (user) {
    return getFollowersByUserId(user._id)
  })
  .then((followers, user) => {
    return {
      user,
      followers
    }
  })

第 3 個 then 的參數是前兩個 then 結果的逆序,即 followers 和 user。更復雜比如嵌套 promise 的我就不列了,這種實現的要點在于:如何區分 then 的層級。從 appoint 的實現我們知道,每個 then 返回一個新的 promise,這導致了無法知道當前 then 來自之前嵌套多深的 promise。所以這個想法無法實現。

命名 Promise

后來,我又想出了一種比上面更好的一種解決方法,即命名 Promise:當前 then 的第一個參數仍然是上個 promise 的返回值(即兼容 Promise/A+ 規范),后面的參數使用依賴注入。體現在代碼上就是:

Promise.resolve()
  .then(function user() {
    return getUserByName('nswbmw')
  })
  .then(function followers(_, user) {
    return getFollowersByUserId(user._id)
  })
  .then((_, user, followers) => {
    return {
      user,
      followers
    }
  })

上面通過給 then 的回調函數命名(如:user),該回調函數的返回值掛載到 promise 內部變量上(如:values: { user: 'xxx'} ),并把父 promise 的 values 往子 promise 傳遞。then 的第二個之后的參數通過依賴注入實現注入,這就是命名 Promise 實現的基本思路。我們可以給 Promise 構造函數的參數、then 回調函數和 catch 回調函數命名。

于是,我在 appoint 包基礎上修改并發布了 named-appoint 包。

named-appoint 原理:給 promise 添加了 name 和 values 屬性,name 是該 promise 的標識(取 Promise 構造函數的參數、then 回調函數或 catch 回調函數的名字),values 是個對象存儲了所有祖先 promise 的 name 和 value。當父 promise 狀態改變時,設置父 promise 的 value 和 values( this.values[this.name] = value),然后將 values 拷貝到子 promise 的 values,依次往下傳遞。再看個例子:

const assert = require('assert')const Promise = require('named-appoint')new Promise(function username(resolve, reject) {
  setTimeout(() => {
    resolve('nswbmw')
  })}).then(function user(_, username) {
  assert(_ === 'nswbmw')
  assert(username === 'nswbmw')
  return {
    name: 'nswbmw',
    age: '17'
  }}).then(function followers(_, username, user) {
  assert.deepEqual(_, { name: 'nswbmw', age: '17' })
  assert(username === 'nswbmw')
  assert.deepEqual(user, { name: 'nswbmw', age: '17' })
  return [
    {
      name: 'zhangsan',
      age: '17'
    },
    {
      name: 'lisi',
      age: '18'
    }
  ]}).then((_, user, followers, username) => {
  assert.deepEqual(_, [ { name: 'zhangsan', age: '17' }, { name: 'lisi', age: '18' } ])
  assert(username === 'nswbmw')
  assert.deepEqual(user, { name: 'nswbmw', age: '17' })
  assert.deepEqual(followers, [ { name: 'zhangsan', age: '17' }, { name: 'lisi', age: '18' } ])}).catch(console.error)

很明顯,命名 Promise 有個前提條件是:在同一條 promise 鏈上。如下代碼:

const assert = require('assert')const Promise = require('named-appoint')new Promise(function username(resolve, reject) {
  setTimeout(() => {
    resolve('nswbmw')
  })}).then(() => {
  return Promise.resolve()
    .then(function user(_, username) {
      assert(username === undefined)
      return {
        name: 'nswbmw',
        age: '17'
      }
    })}).then(function (_, username, user) {
  assert.deepEqual(_, { name: 'nswbmw', age: '17' })
  assert(username === 'nswbmw')
  assert(user === undefined)}).catch(console.error)

最后一個 then 打印 undefined,因為內部產生了一條新的 promise 鏈分支。

結合 co 使用

與 co 結合使用是沒有什么變化的,如:

const Promise = require('named-appoint')const co = require('co')const promise = Promise.resolve()
  .then(function user() {
    return 'nswbmw'
  })
  .then(function followers() {
    return [{ name: 'zhangsan' }, { name: 'lisi' }]
  })
  .then((_, user, followers) => {
    return {
      user,
      followers
    }
  })co(function *() {
  console.log(yield promise)
  /*  { user: 'nswbmw',    followers: [ { name: 'zhangsan' }, { name: 'lisi' } ] }  */}).catch(console.error)

順便擅自制定了一個 Promise/A++ 規范。

『挑剔的』錯誤處理

我們繼續腦洞一下。Swift 中錯誤處理是這樣的:

do {
  try getFollowers("nswbmw")
} catch AccountError.No_User {
  print("No user")
} catch AccountError.No_followers {
  print("No followers")
} catch {
  print("Other error")
}

可以設定 catch 只捕獲特定異常的錯誤,如果之前的 catch 沒有捕獲錯誤,那么錯誤將會被最后那個 catch 捕獲。通過命名 catch 回調函數 JavaScript 也可以實現類似的功能,我在 appoint 的基礎上修改并發布了 condition-appoint 包。看個例子:

var Promise = require('condition-appoint')Promise.reject(new TypeError('type error'))
  .catch(function SyntaxError(e) {
    console.error('SyntaxError: ', e)
  })
  .catch(function TypeError(e) {
    console.error('TypeError: ', e)
  })
  .catch(function (e) {
    console.error('default: ', e)
  })

將會被第二個 catch 捕獲,即打印:

TypeError:  [TypeError: type error]

修改一下:

var Promise = require('condition-appoint')Promise.reject(new TypeError('type error'))
  .catch(function SyntaxError(e) {
    console.error('SyntaxError: ', e)
  })
  .catch(function ReferenceError(e) {
    console.error('ReferenceError: ', e)
  })
  .catch(function (e) {
    console.error('default: ', e)
  })

將會被第三個 catch 捕獲,即打印:

default:  [TypeError: type error]

因為沒有對應的錯誤 catch 函數,所以最終被一個匿名的 catch 捕獲。再修改一下:

var Promise = require('condition-appoint')Promise.reject(new TypeError('type error'))
  .catch(function SyntaxError(e) {
    console.error('SyntaxError: ', e)
  })
  .catch(function (e) {
    console.error('default: ', e)
  })
  .catch(function TypeError(e) {
    console.error('TypeError: ', e)
  })

將會被第二個 catch 捕獲,即打印:

default:  [TypeError: type error]

因為提前被匿名的 catch 方法捕獲。

condition-appoint 實現原理很簡單,就在 appoint 的 then 里加了 3 行代碼:

Promise.prototype.then = function (onFulfilled, onRejected) {
  ...
  if (isFunction(onRejected) && this.state === REJECTED) {
    if (onRejected.name && ((this.value && this.value.name) !== onRejected.name)) {
      return this;
    }
  }
  ...};

判斷傳入的回調函數名和錯誤名是否相等,不是匿名函數且不相等則通過 return this 跳過這個 catch 語句,即實現值穿透。

當然,condition-appoint 對自定義錯誤也有效,只要自定義錯誤設置了 name 屬性。


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

文成县| 若羌县| 理塘县| 永登县| 丰宁| 名山县| 文登市| 苏尼特右旗| 宣武区| 金沙县| 南投县| 邓州市| 雅安市| 海丰县| 德钦县| 临城县| 宝清县| 靖宇县| 漳平市| 平阳县| 泗阳县| 满洲里市| 郎溪县| 永定县| 宁河县| 元氏县| 金平| 湘阴县| 河北区| 县级市| 沂水县| 大厂| 通州市| 阿拉尔市| 青川县| 顺平县| 新乡市| 定西市| 巴林左旗| 潞城市| 丽水市|