当前位置:网站首页>Jsliang job series - 08 - handwritten promise

Jsliang job series - 08 - handwritten promise

2020-11-09 10:51:00 PostgreSQLChina

interviewer : Writing a Promise, Requirement realization resolve()/reject()/then()……

One Catalog

No frills front end , What's the difference with salted fish

Catalog
One Catalog
Two Myth
3、 ... and Simple handwriting Promise
Four Handwriting Promise
5、 ... and Promise.all()
6、 ... and Promise.race()
7、 ... and Promise Asynchronous scheduler
8、 ... and reference

Two Myth

Return directory

JavaScript In the evolution of asynchronous solutions in :

  • callback -> promise -> generator -> async/await

In the computer industry , There is a myth of simple reductionism : That is, the closer we get to the bottom , The higher the technical content .

Every programmer has the pursuit of understanding the underlying source code .

This is true to a certain extent .

however , We should see that too , Once between the bottom and the surface , There's a domain gap .

Master the bottom , It doesn't mean at the surface level .

For example, game developers , Not necessarily the best in the game . This is in FPS Especially in shooting or fighting games , Most of the top players in these games , I can't write code at all .

If you will be proficient in Promise Defined as , Good at using in various asynchronous scenarios Promise solve the problem .

that , Be able to write Promise Realization , Proficient in Promise It doesn't help much .

This text comes from Industrial Agglomeration

3、 ... and Simple handwriting Promise

Return directory
const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';

function myPromise(fn) {
  const that = this;
  that.status = PENDING;
  that.value = null;
  that.reason = null;

  that.resolvedCallbacks = [];
  that.rejectedCallbacks = [];

  function resolve(value) {
    if (that.status === PENDING) {
      that.status = RESOLVED;
      that.value = value;
      that.resolvedCallbacks.map(cb => cb(value));
    }
  }

  function reject(reason) {
    if (that.status === PENDING) {
      that.status = REJECTED;
      that.reason = reason;
      that.rejectedCallbacks.map(cb => cb(reason));
    }
  }

  try {
    fn(resolve, reject);
  } catch(e) {
    reject(e);
  }
}

myPromise.prototype.then = function(onFullfilled, onRejected) {
  const that = this;
  
  if (that.status === PENDING) {
    that.resolvedCallbacks.push(onFullfilled);
    that.rejectedCallbacks.push(onRejected);
  }

  if (that.status === RESOLVED) {
    onFullfilled(that.value);
  }

  if (that.status === RESOLVED) {
    onFullfilled(that.reason);
  }

  return that;
}

const p = new myPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(1000);
  }, 1000);
});

p.then((res) => {
  console.log(' result :', res); //  result :1000
}).then(() => {
  console.log('jsliang'); // jsliang
})

This code is in jsliang This is the personal understanding of :

  1. Yes pendingresolved as well as rejected this 3 Status . See before the code 3 That's ok .
  2. Once the state is changed from pending towards resolve perhaps rejected shift , Then it can't change again . See the code for details. function resolve and function reject.
  3. Code execution to .then When , It is divided into 2 In this case : One is to go asynchronous , The state has become PENDING, Take the first logic ; The second is RESOLVED perhaps RESOLVED, We went through the second and third logic .
  4. In the case of the first logic , Because we're asynchronous , So in the n In seconds function resolve perhaps function reject, And in these two methods ,that.resolvedCallbacks.map(cb => cb(value)) perhaps that.rejectedCallbacks.map(cb => cb(reason)) It will execute the callback method that we have saved , So as to achieve Promise.then() The function of .

If the partner is not clear enough to type the code twice more , If my little friend is right jsliang Not interested in understanding , Look at the references Promise A+ standard , It's more conducive to the understanding of small partners .

Four Handwriting Promise

Return directory

Code source industry gather that article :

At first, I thought the code was very sound , But let jsliang It's really hard for me to write silently , So I used the code from the previous chapter .
/**
 * @name JsliangPromise
 * @description  Handwriting  Promise
 */

/* ———————————————————————————————————————————————  Set common functions  ——————————————————————————————————————————————— */
//  Judge if it's a function 
const isFunction = (obj) => {
  return typeof obj === 'function';
};

//  Judge whether it is the object 
const isObject = (obj) => {
  return !!(obj && typeof obj === 'object');
};

//  Judge whether it is  thenable
const isThenable = (obj) => {
  return (
    isFunction(obj)
    || isObject(obj)
  ) && 'then' in obj;
};

//  Judge whether it is  Promise
const isPromise = (promise) => {
  return promise instanceof JsliangPromise;
};


/* ———————————————————————————————————————————————  Set up  Promise  state  ——————————————————————————————————————————————— */
/**
  1. Promise  Yes  3  Status :pending、fulfilled、rejected
    * pending:Promise  You can switch to  fulfilled  perhaps  rejected  state 
    * fulfilled: Can't migrate to other states , There has to be an immutable  value
    * rejected: Can't migrate to other states , There has to be an immutable  reason
*/
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

/* ——————————————————————————————————————————————— Promise  Supplementary treatment  ——————————————————————————————————————————————— */

// 3.6 handleCallback  function , according to  state  state , Judgment is to go  fulfilled  The path is still  rejected  route 
const handleCallback = (callback, state, result) => {
  // 3.6.1  deconstruction 
  const { onFulfilled, onRejected, resolve, reject } = callback;

  // 3.6.2  Judge 
  try {
    // 3.6.3  If it's successful 
    if (state === FULFILLED) {
      // 3.6.4  Judge  onFulfilled  Is it a function 
      if (isFunction(onFulfilled)) {
        // 3.6.5  If it is , Take its return value as the next  Promise  Of  result
        resolve(onFulfilled(result));
      } else {
        // 3.6.6  If not , Directly to the present  Promise  Of  result  As the next  Promise  Of  result
        resolve(result);
      }
    } else if (state === REJECTED) {
      if (isFunction(onRejected)) {
        resolve(onRejected(result));
      } else {
        reject(result);
      }
    }
  } catch (error) {
    // 3.6.7  If the execution process throws a mistake , Then this mistake , As the next  Promise  Of  rejected reason  To use 
    reject(error);
  }
};

// 2.3.4  Clear the contents before 
const handleCallbacks = (callbacks, state, result) => {
  while (callbacks.length) {
    handleCallback(callbacks.shift(), state, result);
  }
};

// 2.3  Once the state is not  pending, It's not allowed to switch again 
const transition = (promise, state, result) => {
  // 2.3.1  If it is  pending, Then it should be corresponding  state  and  result
  if (promise.state !== PENDING) {
    return;
  }

  // 2.3.2  If not , Then set it up 
  promise.state = state;
  promise.result = result;

  // 2.3.3  When the state changes , Asynchronously empties all  callbacks
  setTimeout(() => {
    handleCallbacks(promise.callbacks, state, result);
  }, 0);
}

// 2.6  Some special  value  By  resolve  when , Special treatment is needed .
//  This special treatment , The specification also clearly describes 
const resolvePromise = (promise, result, resolve, reject) => {
  // 2.6.1  If  result  It is the present.  Promise  In itself , Throw out  TypeError  error 
  if (result === promise) {
    return reject(new TypeError('Can not fulfill promise with itself'));
  }

  // 2.6.2  If  result  Is another  Promise, So use the current  state  and  result  state 
  if (isPromise(result)) {
    return result.then(resolve, reject);
  }

  // 2.6.3  If  result  It's a  thenable  object .
  //  Go first  then  function , Again  call then  function , Re enter  The Promise resolution procedure  The process 
  if (isThenable(result)) {
    try {
      if (isFunction(result.then)) {
        return new JsliangPromise(then.bind(result)).then(resolve, reject);
      }
    } catch (error) {
      return reject(error);
    }
  }

  // 2.6.4  If not , direct  resolve result
  resolve(result);
};

/* ——————————————————————————————————————————————— Promise  Realization  ——————————————————————————————————————————————— */

// 2.  Set up  Promise
const JsliangPromise = function(f) {
  // 2.1  Set the initialization state 
  this.state = PENDING;
  this.result = null;

  // 3.1 .then()  Can be called multiple times , So you need to set the array to record 
  this.callbacks = [];

  // 2.2  structure  onFulfilled  To switch to  fulfilled, structure  onRejected  To switch to  rejected  state 
  const onFulfilled = value => transition(this, FULFILLED, value);
  const onRejected = reason => transition(this, REJECTED, reason);

  // 2.3  coordination  ignore  To guarantee  resolve/reject  There is only one call to function 
  let ignore = false;
  
  // 2.4  Set up  resolve  Treatment mode 
  let resolve = (value) => {
    if (ignore) {
      return;
    }
    ignore = true;
    // 2.5  Yes  resolve  Conduct  3  Rules determine 
    resolvePromise(this, value, onFulfilled, onRejected);
  };

  let reject = (reason) => {
    if (ignore) {
      return;
    }
    ignore = true;
    onRejected(reason);
  }

  // 2.6  Try 
  try {
    // 2.6.1  take  resolve  and  reject  Pass in as a parameter  f  function , Convenient to call 
    f(resolve, reject);
  } catch (error) {
    // 2.6.2  If  f  Function execution error , Then the mistake is taken as  reject  Of  reason  To use 
    reject(error);
  }
}

// 3. Promise.then  Method 
JsliangPromise.prototype.then = function (onFulfilled, onRejected) {
  // 3.2 .then()  Method returns  Promise, So we need to  return  One goes out 
  return new JsliangPromise((resolve, reject) => {

    // 3.3  Set up  callback
    const callback = { onFulfilled, onRejected, resolve, reject };

    // 3.4  If  state  be in  pending  state , Just store it in  callbacks  In the list 
    if (this.state === PENDING) {
      this.callbacks.push(callback);
    } else {
      // 3.5  If not , Just throw one  handleCallback  To deal with 
      //  As for why  setTimeout? Because we can't simulate micro tasks , Then use macro task to solve it 
      setTimeout(() => {
        handleCallback(callback, this.state, this.result);
      }, 0);
    }
  });
};

/* ———————————————————————————————————————————————  test  ——————————————————————————————————————————————— */

const promise = new JsliangPromise((resolve, reject) => {
  setTimeout(() => {
    console.log(1);
    resolve(2);
  }, 1000);
});

promise.then((res) => {
  console.log('res 1:', res);
  return 3;
}).then((res) => {
  console.log('res 2:', res);
});

5、 ... and Promise.all()

Return directory

Let's practice some topics .

Given the content :

const a = new Promise((resolve) => {
  setTimeout(() => {
    resolve(500);
  }, 500);
});
const b = new Promise((resolve) => {
  setTimeout(() => {
    resolve(1000);
  }, 1000);
});

Promise.myAll([a, b]).then((res) => {
  console.log(' result :', res);
});

Request output result :[ 500, 1000 ].

Promise.myAll = function(arr) {
  // 1.  Return to one  Promise
  return new Promise((resolve, reject) => {

    // 2.  Set the final return result 
    const result = [];

    // 3.  Get the length of the array and the current progress index  index
    const length = arr.length;
    let index = 0;

    // 4.  Traversal array , Go through everything in it 
    for (let i = 0; i < arr.length; i++) {

      // 5  stay  .then  It's for  result  Set contents 
      //  If  index  At the end , then  resolve(result)
      //  otherwise  reject(err)
      arr[i].then((res) => {
        result[i] = res;
        
        index++;

        if (index === length) {
          resolve(result);
        }
      }).catch((err) => {
        throw new Error(err);
      })
    }
  })
};

6、 ... and Promise.race()

Return directory

Given the content :

const a = new Promise((resolve) => {
  setTimeout(() => {
    resolve(500);
  }, 500);
});
const b = new Promise((resolve) => {
  setTimeout(() => {
    resolve(1000);
  }, 1000);
});

Promise.myRace([a, b]).then((res) => {
  console.log(' result :', res);
});

Request output result :500.

race Method and previous all The method is similar to , The difference is that it was executed the first time .then It's OK to return the result directly , You don't have to return every result .

Promise.myRace = function(arr) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < arr.length; i++) {
      arr[i].then((res) => {
        return resolve(res);
      }).catch((err) => {
        throw new Error(err);
      })
    }
  })
};

const a = new Promise((resolve) => {
  setTimeout(() => {
    resolve(500);
  }, 500);
});
const b = new Promise((resolve) => {
  setTimeout(() => {
    resolve(1000);
  }, 1000);
});

Promise.myRace([a, b]).then((res) => {
  console.log(' result :', res);
})

7、 ... and Promise Asynchronous scheduler

Return directory

Review the question and complete the following code :

/**
 *  subject :JS  Implement asynchronous scheduler 
 *  requirement :
 *  JS  Implement an asynchronous scheduler with concurrency constraints  Scheduler, The maximum number of tasks guaranteed to run simultaneously is  2  individual 
 *   Perfect the following code  Scheduler  class , Make the program output correctly 
 */

class Scheduler {
  add(promiseCreator) {
    // ...
  }
  // ...
}

const timeout = (time) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, time);
  });
};
const scheduler = new Scheduler();
const addTack = (time, order) => {
  return scheduler
    .add(() => timeout(time))
    .then(() => console.log(order));
};
addTack(1000, '1');
addTack(500, '2');
addTack(300, '3');
addTack(400, '4');

//  Output :2 3 1 4
//  In limine ,1、2  Two tasks enter the queue 
// 500ms  when , complete  2, Output  2, Mission  3  Enter the team 
// 800ms  when , complete  3, Output  3, Mission  4  Enter the team 
// 1000ms  when , complete  1, Output  1, There's no one in the team 
// 1200ms  when , complete  4, Output  4, There's no one in the team 
//  Team entry complete , Output  2 3 1 4

Realization way (async/await):

/**
 *  subject :JS  Implement asynchronous scheduler 
 *  requirement :
 *  JS  Implement an asynchronous scheduler with concurrency constraints  Scheduler, The maximum number of tasks guaranteed to run simultaneously is  2  individual 
 *   Perfect the following code  Scheduler  class , Make the program output correctly 
 */

class Scheduler {
  constructor(maxNum) {
    this.taskList = [];
    this.count = 0;
    this.maxNum = maxNum; //  Maximum number of concurrent 
  }
  async add(promiseCreator) {
    //  If the current concurrency exceeds the maximum concurrency , Then enter the task queue and wait 
    if (this.count >= this.maxNum) {
      await new Promise((resolve) => {
        this.taskList.push(resolve);
      })
    }

    //  frequency  + 1( If the previous one is not finished , Then keep adding )
    this.count++;

    //  Wait for the content to be executed 
    const result = await promiseCreator();

    //  frequency  - 1
    this.count--;

    //  Get the team out of the team first 
    if (this.taskList.length) {
      this.taskList.shift()();
    }

    //  call chaining , Return the result value 
    return result;
  }
}

const timeout = (time) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, time);
  });
};

const scheduler = new Scheduler(2);
const addTack = (time, order) => {
  return scheduler
    .add(() => timeout(time))
    .then(() => console.log(order));
};
addTack(1000, '1');
addTack(500, '2');
addTack(300, '3');
addTack(400, '4');

//  Output :2 3 1 4
//  In limine ,1、2  Two tasks enter the queue 
// 500ms  when , complete  2, Output  2, Mission  3  Enter the team 
// 800ms  when , complete  3, Output  3, Mission  4  Enter the team 
// 1000ms  when , complete  1, Output  1, There's no one in the team 
// 1200ms  when , complete  4, Output  4, There's no one in the team 
//  Team entry complete , Output  2 3 1 4

8、 ... and reference

Return directory

jsliang The document library of is made up of Liang Junrong use Knowledge sharing A signature - Noncommercial use - Share in the same way 4.0 The international license agreement Licensing .<br/> be based on https://github.com/LiangJunrong/document-library Creation of works on .<br/> Use rights other than those authorized by this license agreement can be obtained from https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ To obtain .

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