前端杂谈: 怎样完成一个 Promise?

前端杂谈: 怎样完成一个 Promise?

起首, 什么是 Promise?

A promise is an object that may produce a single value some time in the future: either a resolved value, or a reason that it’s not resolved (e.g., a network error occurred). A promise may be in one of 3 possible states: fulfilled, rejected, or pending. Promise users can attach callbacks to handle the fulfilled value or the reason for rejection.

症结语句: Promise 是一个在未来某个时候发作一个单一效果的对象.
浅显一点来讲, Promise 代表了一个值, 然则这个值我们并不肯定什么时候会被返回.

A promise is an object that may produce a single value some time in the future.

简朴看看 Promise 的汗青

Promise 在 1980 年代被建立出来 =>

在 1988 年正式得名: Promise =>

已有很多人相识到了 Promise, 然则人们照样对峙运用 node.js 中首倡的以回调函数首个参数传 error 对象的体式格局处置惩罚异步代码. =>

Dojo 初次大规模的运用了 Promise , 响应的 Promise/A 被提出用以范例 Promise 的完成 =>

JQuery 最先运用 Promise 并真正使 Promise 广为人知 =>

JQuery 没有完成部份 Promise 的功用, 这也致使了 Promie/A+ 规范的发作 =>

ES6 正式引入了 Promise,而且和已有的完成了 Promise/A 范例的 library 相兼容 =>

完成 Promise 之前, 让我们看看 Promise 有哪些范例

  1. Promise 是一个 thenable 对象, 也就是说 Promise 有一个 .then() 要领
  2. 一个 pending 状况的 Promise 能够进入 fulfilled 和 rejected 状况
  3. promise 一旦进入 fulfilled 或 rejected 状况, 不可再转变其状况
  4. 一旦 promise 转变了其状况, 它笔心有一个值(这个值也多是 undefined)

最先完成一个 Promise

起首, 让我们看看一段最一般的异步代码:

// 异步要领定义
var basicAsyncFunc = function(callback) {
  setTimeout(function() {
    var randomNumber = Math.random()
    if (randomNumber > 0.5) {
      callback(null, randomNumber)
    } else {
      callback(new Error('bad luck...'))
    }
  }, 1000)
}

// 异步要领挪用
basicAsyncFunc((err, result) => {
  if (err) {
    console.log(`the reason fail is: ${err}`)
    return
  }
  console.log(`success get result: ${result}`)
})

依据 Promise 的范例定义, 抱负中 Promise 的挪用体式格局为:

// Promise 情势的异步要领定义
var promiseAsyncFunc = function() {}

// Promise 情势的异步要领挪用
promiseAsyncFunc.then(
  data => {
    console.log(`success get result: ${data}`)
  },
  err => {
    console.log(`the reason fail is: ${err}`)
  }
)

依据这个抱负当中的挪用体式格局, 让我们写出初版代码.

初版 Promise:能保留回调要领

// Promise 情势的异步要领定义
var promiseAsyncFunc = function() {
  var fulfillCallback
  var rejectCallback

  setTimeout(() => {
    var randomNumber = Math.random()
    if (randomNumber > 0.5) fulfillCallback(randomNumber)
    else rejectCallback(randomNumber)
  }, 1000)
  return {
    then: function(_fulfillCallback, _rejectCallback) {
      fulfillCallback = _fulfillCallback
      rejectCallback = _rejectCallback
    }
  }
}

// Promise 情势的异步要领挪用
promiseAsyncFunc().then(fulfillCallback, rejectCallback)

我们的思绪是在 .then() 要领中, 将 fullfill 和 reject 效果的回调函数保留下来, 然后在异步要领中挪用. 由于是异步挪用, 依据 event-loop 的道理, promiseAsyncFunc().then(fulfillCallback, rejectCallback) 传入的 callback 在异步挪用结束时一定是已赋值过了.

第二版 Promise:实组织函数

当前我们的完成 Promise 中,异步逻辑代码和 Promise 的代码是杂糅在一起的,让我们将其区离开:

var promiseAsyncFunc = function() {
  var fulfillCallback
  var rejectCallback

  return {
    fulfill: function(value) {
      if (fulfillCallback && typeof fulfillCallback === 'function') {
        fulfillCallback(value)
      }
    },
    reject: function(err) {
      if (rejectCallback && typeof rejectCallback === 'function') {
        rejectCallback(err)
      }
    },
    then: function(_fulfillCallback, _rejectCallback) {
      fulfillCallback = _fulfillCallback
      rejectCallback = _rejectCallback
    }
  }
}

let ownPromise = function(asyncCall) {
  let promise = promiseAsyncFunc()
  asyncCall(promise.fulfill, promise.reject)
  return promise
}

// Promise 情势的异步要领挪用
ownPromise(function(fulfill, reject) {
  setTimeout(() => {
    var randomNumber = Math.random()
    if (randomNumber > 0.5) fulfill(randomNumber)
    else reject(randomNumber)
  }, 1000)
})

我们新定义了一个要领 ownPromise() 用于建立 Promise,并在promiseAsyncFunc() 中暴露出 fulfillreject 接口轻易异步代码去挪用。

这里有一个题目,我们在挪用 ownPromise()后得到了 promise 实例,此时我们能够直接挪用 fulfill()reject()这两个要领,而理论上我们应当只应暴露 promise 的then()要领。所以我们应用闭包将这两个要领隐蔽:

var promiseAsyncFunc = function() {
  var fulfillCallback
  var rejectCallback

  return {
    fulfill: function(value) {
      if (fulfillCallback && typeof fulfillCallback === 'function') {
        fulfillCallback(value)
      }
    },
    reject: function(err) {
      if (rejectCallback && typeof rejectCallback === 'function') {
        rejectCallback(err)
      }
    },
    promise: {
      then: function(_fulfillCallback, _rejectCallback) {
        fulfillCallback = _fulfillCallback
        rejectCallback = _rejectCallback
      }
    }
  }
}

let ownPromise = function(asyncCall) {
  let defer = promiseAsyncFunc()
  asyncCall(defer.fulfill, defer.reject)
  return defer.promise
}

// Promise 情势的异步要领挪用
ownPromise(function(fulfill, reject) {
  setTimeout(() => {
    var randomNumber = Math.random()
    if (randomNumber > 0.5) fulfill(randomNumber)
    else reject(randomNumber)
  }, 1000)
})

第三版 Promise: 支撑状况治理

为了完成范例中关于 Promise 状况变化的请求, 我们需要为 Promise 到场状况治理, 这一步较为简朴, 让我们看代码:

const PENDING = Symbol('pending')
const FULFILLED = Symbol('fulfilled')
const REJECTED = Symbol('rejected')

// Promise 情势的异步要领定义
var promiseAsyncFunc = function() {
  var status = PENDING
  var fulfillCallback
  var rejectCallback

  return {
    fulfill: function(value) {
      if (status !== PENDING) return
      if (typeof fulfillCallback === 'function') {
        fulfillCallback(value)
        status = FULFILLED
      }
    },
    reject(error) {
      if (status !== PENDING) return
      if (typeof rejectCallback === 'function') {
        rejectCallback(error)
        status = REJECTED
      }
    },
    promise: {
      then: function(_fulfillCallback, _rejectCallback) {
        fulfillCallback = _fulfillCallback
        rejectCallback = _rejectCallback
      }
    }
  }
}

let ownPromise = function(asyncCall) {
  let defer = promiseAsyncFunc()
  asyncCall(defer.fulfill, defer.reject)
  return defer.promise
}

// Promise 情势的异步要领挪用
ownPromise(function(fulfill, reject) {
  setTimeout(() => {
    var randomNumber = Math.random()
    if (randomNumber > 0.5) fulfill(randomNumber)
    else reject(randomNumber)
  }, 1000)
}).then(data => console.log(data), err => console.log(err))

这段代码中我们用到了 Symbol 来示意状况常量, 对 Symbol 不相识的同砚能够看这里

为了推断 Promise 的状况, 我们到场了 fulfillreject 两个要领。并在个中推断 promise 当前状况。假如不是 pending 状况则直接 return(由于 Promise 状况只能够转变一次)。

如今我们的 promise 完成了对状况掌握的范例:

  • 只允许转变一次状况
  • 只能从 pending => fulfilled 或 pending => rejected

然则我们的 Promise 有一个题目: promise 的值没有被保留下来。假如 promise 在异步挪用完成以后才被挪用 .then() 要领,则我们没法把异步挪用的效果传递给回调函数。为此我们需要为 Promise 加一个 value 字段:

第四版 Promise: 保留异步挪用的效果

我们为 promise 到场 value 字段,用于保留 Promise 的实行效果。

// Promise 情势的异步要领定义
var promiseAsyncFunc = function() {
  var status = PENDING
  var fulfillCallback
  var rejectCallback
  var value

  return {
    fulfill: function(_value) {
      if (status !== PENDING) return
      value = _value
      status = FULFILLED
      if (typeof fulfillCallback === 'function') {
        fulfillCallback(value)
      }
    },
    reject(error) {
      if (status !== PENDING) return
      value = error
      status = REJECTED
      if (typeof rejectCallback === 'function') {
        rejectCallback(error)
      }
    },
    promise: {
      then: function(_fulfillCallback, _rejectCallback) {
        fulfillCallback = _fulfillCallback
        rejectCallback = _rejectCallback
      }
    }
  }
}

这里我们又发明一个题目,假如一个 Promise 已是fulfillreject状况。我们再挪用 then() 要领时,传入的回调要领永久不会被挪用(由于 status 已不是 pending)。

所以我们需要在 then()要领中对其状况举行推断:

// Promise 情势的异步要领定义
var promiseAsyncFunc = function() {
  var status = PENDING
  var fulfillCallback
  var rejectCallback
  var value

  return {
    fulfill: function(_value) {
      if (status !== PENDING) return
      value = _value
      status = FULFILLED
      if (typeof fulfillCallback === 'function') {
        fulfillCallback(value)
      }
    },
    reject(error) {
      if (status !== PENDING) return
      value = error
      status = REJECTED
      if (typeof rejectCallback === 'function') {
        rejectCallback(error)
      }
    },
    promise: {
      then: function(_fulfillCallback, _rejectCallback) {
        if (status === REJECTED) {
          _rejectCallback(value)
          return
        }
        if (status === FULFILLED) {
          _fulfillCallback(value)
          return
        }
        fulfillCallback = _fulfillCallback
        rejectCallback = _rejectCallback
      }
    }
  }
}

第五版 Promise: 支撑链式挪用

为了支撑链式挪用,.then() 要领的返回值必需是用 thenable (依据 Promise/A+ 范例, .then() 要领的返回值需如果一个的 Promise)

为此我们到场一个东西要领 makeThenable()。假如传入的 value 自身就有 then()要领,则直接返回 value。不然返回一个有 then()要领的对象。
在该对象的 then()要领中,我们依据 promise 的状况,挪用差别的回调要领天生新的 value。

function makeThenable(value, status){
  if(value && typeof value.then === 'function'){
    return value
  }
  if(status === FULFILLED){
    return {
      then: function(fulfillCallback, rejectCallback){
        return makeThenable(fulfillCallback(value), FULFILLED)
      }
    }
  }
  if(status === REJECTED) {
    return {
      then: function(fulfillCallback, rejectCallback){
        return makeThenable(rejectCallback(value), FULFILLED)
      }
    }
  }
}

有了以上的 makeThenable()要领,我们能够在 promise 的fulfill()reject()回将 value 设置为 thenable:

var promiseAsyncFunc = function() {
  var status = PENDING
  var fulfillCallback
  var rejectCallback
  var value

  return {
    fulfill: function(_value) {
      if (status !== PENDING) return
      value = makeThenable(_value, FULFILLED) // 保证当前promise的value为 thenable
      status = FULFILLED
      if (typeof fulfillCallback === 'function') {
        value.then(fulfillCallback)
      }
    },
    reject(error) {
      if (status !== PENDING) return
      value = makeThenable(error, REJECTED) 、、  // 保证当前value为 thenable
      status = REJECTED
      if (typeof rejectCallback === 'function') {
        value.then(null, rejectCallback)
      }
    },
    promise: {
      then: function(){}
    }
  }
}

接下来让我们看 then()要领。为了返回一个新的 promise,我们起首得建立一个新的 promise。其次当前 promise 在fulfill()reject()时,应当挪用新的 promise 的fullfill()reject()要领。所以我们在将 fulfullCallbackrejectCallback赋值给当前 promise 时,将其包装一下。代码以下:

    promise: {
      then: function(_fulfillCallback, _rejectCallback) {
        let newPromiseAsyncFunc = promiseAsyncFunc()
        let fulfillFunc = function(value) {
          newPromiseAsyncFunc.fulfill(_fulfillCallback(value))
        }
        let rejectFunc = function(err) {
          newPromiseAsyncFunc.fulfill(_rejectCallback(err))
        }
        if (status === PENDING) {
          fulfillCallback = fulfillFunc
          rejectCallback = rejectFunc
        } else {
          value.then(fulfillFunc, rejectFunc)
        }
        return newPromiseAsyncFunc.promise
      }
    }

云云,我们变得到了一个能够链式挪用的 promise。让我们来测试一下:

const PENDING = Symbol('pending')
const FULFILLED = Symbol('fulfilled')
const REJECTED = Symbol('rejected')

function makeThenable(value, status) {
  if (value && typeof value.then === 'function') {
    return value
  }
  if (status === FULFILLED) {
    return {
      then: function(fulfillCallback, rejectCallback) {
        return makeThenable(fulfillCallback(value), FULFILLED)
      }
    }
  }
  if (status === REJECTED) {
    return {
      then: function(fulfillCallback, rejectCallback) {
        return makeThenable(rejectCallback(value), FULFILLED)
      }
    }
  }
}

// Promise 情势的异步要领定义
var promiseAsyncFunc = function() {
  var status = PENDING
  var fulfillCallback
  var rejectCallback
  var value

  return {
    fulfill: function(_value) {
      if (status !== PENDING) return
      value = makeThenable(_value, FULFILLED)
      status = FULFILLED
      if (typeof fulfillCallback === 'function') {
        value.then(fulfillCallback)
      }
    },
    reject(error) {
      if (status !== PENDING) return
      value = makeThenable(error, REJECTED)
      status = REJECTED
      if (typeof rejectCallback === 'function') {
        value.then(null, rejectCallback)
      }
    },
    promise: {
      then: function(_fulfillCallback, _rejectCallback) {
        let newPromiseAsyncFunc = promiseAsyncFunc()
        let fulfillFunc = function(value) {
          newPromiseAsyncFunc.fulfill(_fulfillCallback(value))
        }
        let rejectFunc = function(err) {
          newPromiseAsyncFunc.fulfill(_rejectCallback(err))
        }
        if (status === PENDING) {
          fulfillCallback = fulfillFunc
          rejectCallback = rejectFunc
        } else {
          value.then(fulfillFunc, rejectFunc)
        }
        return newPromiseAsyncFunc.promise
      }
    }
  }
}

let ownPromise = function(asyncCall) {
  let defer = promiseAsyncFunc()
  asyncCall(defer.fulfill, defer.reject)
  return defer.promise
}

let testChainedPromise = ownPromise(function(fulfill, reject) {
  setTimeout(() => {
    var randomNumber = Math.random()
    if (randomNumber > 0.5) fulfill(randomNumber)
    else reject(randomNumber)
  }, 1000)
})
  .then(
    data => {
      console.log(data)
      return 'return value in then1 fulfill'
    },
    err => {
      console.log(err)
      return 'return value in then1 reject'
    }
  )
  .then(
    data => {
      console.log(data)
      return 'return value in then2 fulfill'
    },
    err => {
      console.log(err)
      return 'return value in then2 reject'
    }
  )
  .then(
    data => {
      console.log(data)
    },
    err => {
      console.log(err)
    }
  )

/**
console output:

0.9931984611850693
return value in then1 fulfill
return value in then2 fulfill
*/

第六版 Promise: Error handling

这里我们只对
异步挪用
fulfill 回调中抛出的 error 举行处置惩罚。

起首是异步挪用部份,我们将其 try catch 起来,在发作非常时挪用 reject 要领,并将非常作为参数传入。

let ownPromise = function(asyncCall) {
  let defer = promiseAsyncFunc()
  try {
    asyncCall(defer.fulfill, defer.reject)
  } catch (e) {
    defer.reject(e)
  }
  return defer.promise
}

然后是 fulfill 中能够涌现的非常。我们对fulfillCallback(value)能够涌现的非常举行捕捉,并将非常传递给rejectCallback

function makeThenable(value, status) {
  if (value && typeof value.then === 'function') {
    return value
  }
  if (status === FULFILLED) {
    return {
      then: function(fulfillCallback, rejectCallback) {
        try {
          let newValue = fulfillCallback(value)
          return makeThenable(newValue, FULFILLED)
        } catch (e) {
          return makeThenable(rejectCallback(e), FULFILLED)
        }
      }
    }
  }
  if (status === REJECTED) {
    return {
      then: function(fulfillCallback, rejectCallback) {
        return makeThenable(rejectCallback(value), FULFILLED)
      }
    }
  }
}

末了让我们对完全的代码举行测试:

const PENDING = Symbol('pending')
const FULFILLED = Symbol('fulfilled')
const REJECTED = Symbol('rejected')

function makeThenable(value, status) {
  if (value && typeof value.then === 'function') {
    return value
  }
  if (status === FULFILLED) {
    return {
      then: function(fulfillCallback, rejectCallback) {
        try {
          let newValue = fulfillCallback(value)
          return makeThenable(newValue, FULFILLED)
        } catch (e) {
          return makeThenable(rejectCallback(e), FULFILLED)
        }
      }
    }
  }
  if (status === REJECTED) {
    return {
      then: function(fulfillCallback, rejectCallback) {
        return makeThenable(rejectCallback(value), FULFILLED)
      }
    }
  }
}

// Promise 情势的异步要领定义
var promiseAsyncFunc = function() {
  var status = PENDING
  var fulfillCallback
  var rejectCallback
  var value

  return {
    fulfill: function(_value) {
      if (status !== PENDING) return
      value = makeThenable(_value, FULFILLED)
      status = FULFILLED
      if (typeof fulfillCallback === 'function') {
        value.then(fulfillCallback)
      }
    },
    reject(error) {
      if (status !== PENDING) return
      value = makeThenable(error, REJECTED)
      if (typeof rejectCallback === 'function') {
        value.then(null, rejectCallback)
      }
      status = REJECTED
    },
    promise: {
      then: function(_fulfillCallback, _rejectCallback) {
        let newPromiseAsyncFunc = promiseAsyncFunc()
        let fulfillFunc = function(value) {
          newPromiseAsyncFunc.fulfill(_fulfillCallback(value))
        }
        let rejectFunc = function(err) {
          newPromiseAsyncFunc.fulfill(_rejectCallback(err))
        }
        if (status === PENDING) {
          fulfillCallback = fulfillFunc
          rejectCallback = rejectFunc
        } else {
          value.then(fulfillFunc, rejectFunc)
        }
        return newPromiseAsyncFunc.promise
      }
    }
  }
}

let ownPromise = function(asyncCall) {
  let defer = promiseAsyncFunc()
  try {
    asyncCall(defer.fulfill, defer.reject)
  } catch (e) {
    defer.reject(e)
  }
  return defer.promise
}

let testChainedPromise = ownPromise(function(fulfill, reject) {
  throw Error('here is an error in asyncCall')
  setTimeout(() => {
    var randomNumber = Math.random()
    if (randomNumber > 0.5) fulfill(randomNumber)
    else reject(randomNumber)
  }, 1000)
})
  .then(
    data => {
      console.log(data)
      return 'return value in then1 fulfill'
    },
    err => {
      console.log(err.message)
      return 'return value in then1 reject'
    }
  )
  .then(
    data => {
      console.log(data)
      throw Error('here is an error in fulfill1')
      return 'return value in then2 fulfill'
    },
    err => {
      console.log(err.message)
      return 'return value in then2 reject'
    }
  )
  .then(
    data => {
      console.log(data)
    },
    err => {
      console.log(err.message)
    }
  )


// console out:
Error: here is an error in asyncCall
return value in then1 reject
Error: here is an error in fulfill1
return value in then2 reject

总结

以上就是我们关于 Promise 的一个简朴的完成,完成思绪重要参考了 Q-A promise library for javascript。该完成的 Promise 功用较为大略,仅完成了部份 api/范例。有任何看法和发起迎接在批评区交换 ;)

进一步浏览 && 援用

关于 Promise 运用以及error handle 的解说:

https://medium.com/javascript…

Promise 完成库之一: Q 关于 Promise 完成的解说:

https://github.com/kriskowal/…

想相识更多 前端 和 数据可视化 ?

这里是我的 前端、_D3.js_ 、 数据可视化 的 github 地点, 迎接 star & fork

ssthouse-blog

假如以为本文不错的话, 无妨点击下面的链接关注一下 : )

github 主页

知乎专栏

掘金

想直接联络我 ?

邮箱: ssthouse@163.com

    原文作者:ssthouse
    原文地址: https://segmentfault.com/a/1190000017367259
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞