当前位置:网站首页>On promiz

On promiz

2020-11-10 11:24:47 Tao Baba

One 、Promiz What is it? ?

1、Promiz


  • A proper compact promise (promises/A+ spec compliant) library
  • A polyfill for ES6-style Promises in 913 bytes (gzip)

( A very small one promise library )

2、Promise/A+ What is the norm ?


Promise/A+ The purpose of the specification is to Promise Provide an interactive then function
The reason for the specification

1、 We don't know when an asynchronous request returns data , So we need some callback functions . But in some cases we need to return data , Then do some processing .
2、 When the operation we handle in an asynchronous callback is still asynchronous , This results in the nesting of asynchronous callbacks
It is in order to put an end to the above two situations , There's a community Promise/a+ standard

Content of specification

1、 Whatever you do, it returns a promise object , This object will have some properties and methods ( The effect is similar to jquery Chain programming in , Go back to yourself )
2、 This promise There are three states

  • Unfulfilled( Hang in the air , The initial state )
  • Fulfilled( Completed )
  • Failed( Failure 、 Refuse )

3、 This promise Objects are used by then Method
image.png

3、Polyfill What is it? ?


Polyfill It mainly smoothes the relationship between different browsers js Differences in implementation . such as ,html5 Of storage(session,local), Different browsers , Different versions , Some support , Some don't .Polyfill You can enable unsupported browsers to support Storage( The typical way is in IE Add... To the browser window.XMLHttpRequest , Internal implementation uses ActiveXObject.)

polyfill It's a piece of code ( Or plug-ins ), It provides features that developers want their browsers to support natively . The library first checks whether the browser supports a certain API, If it is not supported, the corresponding polyfill. Main features :

  • Is a browser API Of Shim
  • It's about browsers
  • No new API, It's just API To implement the missing functions in
  • Just import polyfill , It will work quietly

mention Polyfill, Have to mention shim,polyfill yes shim A kind of .

shim The concept of is better than polyfill Bigger , Can be polyfill Specifically compatible with browsers API Of shim .shim It's going to be different api Package it into a kind of , such as jQuery Of $.ajax Encapsulates the XMLHttpRequest and IE use ActiveXObject Way to create xhr object . It will be a new API Introduce into an old environment , And only by means of the old environment . To put it simply , If the browser X Support the functions specified in the standard , that polyfill You can make the browser Y Behavior and browser X equally .

Two 、Promiz How to use it? ?

bower install promiz --save
<!-- Browser -->
<script src='promiz.js'></script>

Commonly used API:

  • new Promise(Function<resolve, reject>)
  • Promise.reject({reason})
  • Promise.resolve({value})
  • promise.then({Function}, {Function})
  • promise.catch({Function})
  • Promise.all({iterable})
  • Promise.race({iterable})

3、 ... and 、Promiz The source code parsing

promiz.js file :

This file contains an immediate function , This includes constructors Deferred, The final will be Deferred export , As node.js Library objects for , Or as global variables . Now look at the code in the file :

if (!global.setImmediate)
    global.addEventListener('message', function (e) {
      if (e.source == global){
        if (isRunningTask)
          nextTick(queue[e.data])
        else {
          isRunningTask = true
          try {
            queue[e.data]()
          } catch (e) {}

          delete queue[e.data]
          isRunningTask = false
        }
      }
    })

The above code listens to message event , Execute asynchronous functions in the queue , among nextTick The method code is as follows , Mainly compatible with various browsers to implement asynchronous function

function nextTick(fn) {
    if (global.setImmediate) setImmediate(fn)
    // if inside of web worker   If in Web Worker The following methods are used in 
    else if (global.importScripts) setTimeout(fn)
    else {
      queueId++
      queue[queueId] = fn
      global.postMessage(queueId, '*')
    }
  }

The above code is mainly promiz adopt setImmediate、setTimeout and postMessage Three ways to execute asynchronous functions . If you want to execute a function asynchronously , The first way we'll think of it will be setTimeout, In order to avoid setTimeout Nesting may get stuck ui Thread situation , by setTimeout Set the minimum execution interval , Different browsers have different minimum execution intervals .chrome Next test setTimeout 0 The actual execution interval of is about 12ms about .

Want to execute a function asynchronously the fastest , have access to setImmediate Method , This method is more effective than setTimeout 0 Faster asynchronous execution , Execution time is closer to 0ms, But only IE Browser support .

In addition to using asynchronous functions , There are also ways to implement asynchronous calls . utilize onmessage: and iframe When communicating, we often use onmessage Method , But if the same window postMessage Give yourself , What will happen? ? In fact, it is also equivalent to executing an asynchronously function.

PostMessage yes H5 The new method in ,setTimeout Best compatibility , It can be used in a variety of scenarios , So in the code above you can use setTimeout Make the bottom of the bag , Ensure that all kinds of browsers can execute asynchronous functions normally .

Let's look at the constructor code :

function Deferred(resolver) {
    'use strict'
    if (typeof resolver != 'function' && resolver != undefined)
      throw TypeError()

    if (typeof this != 'object' || (this && this.then))
      throw TypeError()
    
    // states
    // 0: pending
    // 1: resolving
    // 2: rejecting
    // 3: resolved
    // 4: rejected
    var self = this,
      state = 0,
      val = 0,
      next = [],
      fn, er;

    self['promise'] = self
    ...
   }

The constructor first stores Promise The state of ( use 0-4 It's five states )、Promise The success value or failure reason of 、 next Promise References to 、Promise Of then The success and failure callback functions in method .

 self['resolve'] = function (v) {
      fn = self.fn
      er = self.er
      if (!state) {
        val = v
        state = 1

        nextTick(fire)
      }
      return self
    }

    self['reject'] = function (v) {
      fn = self.fn
      er = self.er
      if (!state) {
        val = v
        state = 2

        nextTick(fire)

      }
      return self
    }

After storing the data, I declare Promise Of resolve and reject function , In both functions, it changed state Value , And then through nextTick Methods the trigger fire Asynchronous call .

self['then'] = function (_fn, _er) {
      if (!(this._d == 1))
        throw TypeError()

      var d = new Deferred()

      d.fn = _fn
      d.er = _er
      if (state == 3) {
        d.resolve(val)
      }
      else if (state == 4) {
        d.reject(val)
      }
      else {
        next.push(d)
      }

      return d
    }

    self['catch'] = function (_er) {
      return self['then'](null, _er)
    }

The statement Promise Of then and catch Method , stay then Methods by judging state To determine the current Promise What method to carry out : If state Show Promise become resolved state , So do it immediately resolve, If state Show Promise become rejected state , So do it immediately reject, If neither is , Just put then The two parameters of the method are used as the new Promise Of resolve and reject Method , And return a new Promise.Promise Of catch Method by calling then Method , And set the first parameter to null Realization , namely Promise perform resolve after catch Methods do not process , however Promise perform reject after , Call the passed in _er Method to handle the error .

Here's how fire Method :

 function fire() {

      // check if it's a thenable
      var ref;
      try {
        ref = val && val.then
      } catch (e) {
        val = e
        state = 2
        return fire()
      }

      thennable(ref, function () {
        state = 1
        fire()
      }, function () {
        state = 2
        fire()
      }, function () {
        try {
          if (state == 1 && typeof fn == 'function') {
            val = fn(val)
          }

          else if (state == 2 && typeof er == 'function') {
            val = er(val)
            state = 1
          }
        } catch (e) {
          val = e
          return finish()
        }

        if (val == self) {
          val = TypeError()
          finish()
        } else thennable(ref, function () {
            finish(3)
          }, finish, function () {
            finish(state == 1 && 3)
          })

      })
    }

As you can see from the code ,fire It is mainly used to judge ref Is it a thenable object , And then it calls thenable function , Pass the 3 Callback functions . So let's see thennable What does the method do

// ref : reference to 'then' function  Point to thenable Object's `then` function 
    // cb, ec, cn : successCallback, failureCallback, notThennableCallback
    function thennable (ref, cb, ec, cn) {
      if (state == 2) {
        return cn()
      }
      if ((typeof val == 'object' || typeof val == 'function') && typeof ref == 'function') {
        try {

          // cnt protects against abuse calls from spec checker
          var cnt = 0
          ref.call(val, function (v) {
            if (cnt++) return
            val = v
            cb()
          }, function (v) {
            if (cnt++) return
            val = v
            ec()
          })
        } catch (e) {
          val = e
          ec()
        }
      } else {
        cn()
      }
    };

stay thennable In the method , First judgement , If ref Of state The value is 2 That is to say Promise The state of is rejecting, Just do it directly cn Method , Direct delivery ref Of reject state . When ref No thennable Object time , It's also direct execution cn Method . When ref Of state It's not worth it 2, And ref yes thennable Object time , Through the variable cnt To record ref The state of , According to the state value to execute separately cb and ec Method , That is to say, separate execution ref Of resolve Methods and reject Method .

Let's introduce Deferred Of API:

Deferred.all = function (arr) {
    if (!(this._d == 1))
      throw TypeError()

    if (!(arr instanceof Array))
      return Deferred.reject(TypeError())

    var d = new Deferred()

    function done(e, v) {
      if (v)
        return d.resolve(v)

      if (e)
        return d.reject(e)

      var unresolved = arr.reduce(function (cnt, v) {
        if (v && v.then)
          return cnt + 1
        return cnt
      }, 0)

      if(unresolved == 0)
        d.resolve(arr)

      arr.map(function (v, i) {
        if (v && v.then)
          v.then(function (r) {
            arr[i] = r
            done()
            return r
          }, done)
      })
    }

    done()

    return d
  }

The code implements Deferred Of all Interface , This interface gives each of the arrays Promise All increase then Method , And pass cnt In the array of variable pairs Promise Of resolved Count the number of , When all the quantities become resolved Post state , perform resolve Method . When any one of them Promise become rejected state , perform reject Method .

Deferred.race = function (arr) {
    if (!(this._d == 1))
      throw TypeError()

    if (!(arr instanceof Array))
      return Deferred.reject(TypeError())

    if (arr.length == 0)
      return new Deferred()

    var d = new Deferred()

    function done(e, v) {
      if (v)
        return d.resolve(v)

      if (e)
        return d.reject(e)

      var unresolved = arr.reduce(function (cnt, v) {
        if (v && v.then)
          return cnt + 1
        return cnt
      }, 0)

      if(unresolved == 0)
        d.resolve(arr)

      arr.map(function (v, i) {
        if (v && v.then)
          v.then(function (r) {
            done(null, r)
          }, done)
      })
    }

    done()

    return d
  }

Promise Of race Interface and all Similar interface , Also by giving each of the arrays Promise All increase then Method , And pass cnt In the array of variable pairs Promise Of resolved Count the number of , The difference is race Method pairs will first become resolved State of Promise Conduct resolve.

Four 、 summary

Promiz The source code is concise and easy to read , It mainly contains a constructor to create Promise example , Instance implements the compatible execution of asynchronous functions , And defined Promise Of resolve、reject、all、race Such as the interface , Hundreds of lines of code solved the problem of calling back to hell , The structure and logic are clear .

版权声明
本文为[Tao Baba]所创,转载请带上原文链接,感谢