[Front-end Talk _2] Asynchronous Programming from Effect of Dva to Generator + Promise


What can you learn?

  • How to useGenerator + PromiseImplementing Asynchronous Programming
  • Principle Analysis of Asynchronous Programming


In conjunction with the previous article, let’s talk about it.Generator

Basic principles

When it comes to asynchronous programming, what you think about isasyncandawaitBut that’s justGeneratorIt’s just grammatical sugar. There is one in DVAEffectThe concept is to useGeneratorTo solve the problem of asynchronous requests, let’s talk about it.Generator + PromiseHow to program asynchronously:

Before we begin, we need to understand some basic concepts:

  • GeneratorAct asES6The specific implementation of asynchronous programming is handled by using the solution of the protocol, which is characterized by:GeneratorCan be used inyieldExamples of keyword matchinggencallnext()Method to split and execute its internal statements. In short:next()Called once, thenyieldThe statement is executed with a sentence.next()Call,yieldStatements are executed sequentially.
  • PromiseRepresents the final state (completion or failure) of an asynchronous operation and the value it returns. Refer to Promise-MDN

So asynchronous programming usesGeneratorandPromiseWhat is the principle of implementation?

  1. becauseGeneratoritselfyieldStatements are executed separately, so we take advantage of this inyieldReturns aPromiseobject
  2. First callGeneratorMediumnext()Then, suppose the return value is calledresultSo at this pointresult.valueThat’s what we define inyieldStatementPromiseobject

Note: In this step, we have suspended the original execution process and turned to execution.PromiseContent, has been implemented to control the execution of asynchronous code, because at this point if we do not continue to executenext()begeneratorIn the current executionyieldThe latter will not continue to be implemented, which has achieved the desired results.

  1. Next we’re going to finish the current implementation.PromiseAfter that, let the code continue to execute until you encounter the next oneyieldSentence:

    This is the most critical step.So what should we do?

    Step 1: At presentPromiseOfthen()In the method, continue to executegen.next()

    Step 2:gen.next()Return resultsresult.done === trueWhen we get it, we get it.result.valueThat’s a new one.PromiseObject) Executed again and in itsthen()Continue step 1 above in the method untilresult.done === falseWhen. Call at this timeresolve()sendpromiseThe state changes because of allyieldThe statement has been executed.

    • Step 1 ensures that we can move on to the next step.yieldSentence
    • Step 2 ensures the nextyieldStatements are executed without interruption untilGeneratorThe last of themyieldThe statement is executed.

Flow diagram:

Concrete realization

Co is implemented by the famous God TJGeneratorSecondary encapsulation libraries, then we will fromcoStarting with a demo in the library, learn about our entire asynchronous request encapsulation implementation:

co(function*() {
    yield me.loginAction(me.form);

Here we introducecoLibrary, and usecoTo wrap up onegenerator(Generator) Object.

Now let’s look at it.coFor wrappedgeneratorWhat has been done?

function co(gen) {
  // 1. Get the execution context of the current co function and get the parameter list
  var ctx = this;
  var args = slice.call(arguments, 1);
  // 2. Return a Promise object
  return new Promise(function(resolve, reject) {
    // Determine and initialize the generator using ctx: context and arg: arguments (parameter list) and copy it to Gen
    // Note:
    // After Gen = Gen. apply (ctx, args)
    // When we call Gen. next (), we return a pointer, and the actual value is an object.
    // Object form: {done:[false | true], value:'}
    if (typeof gen === 'function') gen = gen.apply(ctx, args);
    // When the return value is not Gen or the type of Gen. next is not function [actually to determine whether it is generator]
    // The current promise state is set to resolve and ends
    if (!gen || typeof gen.next !== 'function') return resolve(gen);
    // Otherwise, onFulfilled ()

Summarize what happened here

  1. Return to onepromise
  2. promiseWill be wrapped upgeneratorInstance into a pointer, pointinggeneratorFirst in ChinayieldSentence
  3. judgegeneratorDoes the instantiated pointer exist: if notyieldStatement, pointer does not exist
    Judgement pointergen.next()Whether the method isfunctionIf notfunctionProve that it is not enforceablegen.next()
    If one condition is not satisfied, we willpromiseThe state is set toresolve
    Otherwise executiononFulfilled()

Now let’s look at it.onFulfilled()Implementation

function onFulfilled(res) {
      // When onFulfilled is executed, a RET is defined to store the pointer object after Gen. next (res) execution.
      var ret;
      try {
        ret = gen.next(res);
      // Here, the value thrown by the yield statement is {value: me. loginAction (me. form), done: false}.
      } catch (e) {
        return reject(e);
    // Pass the RET object into the next method we defined in promise
      return null;

To sum up,onFulfilledThe main job is

  1. implementgen.next()Enable code to execute toyieldSentence
  2. Pass the results returned after execution into our customnext()Method medium

So let’s look at it again.next()Method

function next(ret) {
    // In next, we first judge the done state of RET we passed in:
    // Case 1: ret. done = true means that all yield statements in our generator have been executed.
    // Then, by passing ret. value into resolution (), the state of promise becomes solution, and the whole process ends.
      if (ret.done) return resolve(ret.value);
    // Case 2: Currently ret. done = false represents that generator has not executed all yield statements, so at this time
    // We pass the current context and ret. value into toPromise and convert it to the corresponding Promise object `value'.`
      var value = toPromise.call(ctx, ret.value);
      if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
    // When value is indeed a promise object, return value. then (onFulfilled, onRejected)
    // We re-enter the generator and execute the next yield statement
      return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
        + 'but the following object was passed: "' + String(ret.value) + '"'));

To sum up,nextMain work

  1. Judge last timeyieldExecution result of statement
  2. takeyieldOfresultOfvalueValue [Actually, we’re going to execute it asynchronouslyPromise
  3. implementvalueOfthenMethod, reentryonFulfilledIn the method, theonFulfilledWe will enter the current method again, so that the call of the loop is implemented.generatorandPromiseThe execution switch is implemented.PromiseThe content is executed in the order we define.

Some students may be right here.toPromiseThere are some doubts about the method. Let me paste out the code first.

function toPromise(obj) {
  if (!obj) return obj;
  if (isPromise(obj)) return obj;
  if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
  if ('function' == typeof obj) return thunkToPromise.call(this, obj);
  if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
  if (isObject(obj)) return objectToPromise.call(this, obj);
  return obj;

In fact, what this function does is convert according to different types so that the final output type is the same.Promise。 That specific conversion details, you can refer to the source code of the CO library.

So far, the control of asynchronous operation is realized.


This is Dendoink, the original creator of Qiwu Weekly and the Nuggets [Co-Editor/Booklet Author].
For technicians, technology is the individual combat ability, while skill is the method of using ability. Art is the art of being handy and being fascinated. In the front entertainment circle, I want to be an excellent people’s artist. I’m here to wait for you.

Recommended Today

Hadoop MapReduce Spark Configuration Item

Scope of application The configuration items covered in this article are mainly for Hadoop 2.x and Spark 2.x. MapReduce Official documents https://hadoop.apache.org/doc…Lower left corner: mapred-default.xml Examples of configuration items name value description mapreduce.job.reduce.slowstart.completedmaps 0.05 Resource requests for Reduce Task will not be made until the percentage of Map Task completed reaches that value. mapreduce.output.fileoutputformat.compress false […]