Promise.all

Promise. All methods

  • Receive an array of Promises
  • Return a Promise withpThe cache

After all the promises passed to promise.all are fulfilled, P follows suit and returns an array of the results of each Promise fulfilled in incoming order

However, as soon as a Promise is rejected, P will immediately identify the rejection as the reason for the rejection of the Promise.

When multiple requests are made asynchronously at the same time, that is, in parallel, all is used

let p1 = new Promise((resolve, reject) = > {
  setTimeout((a)= > {
    resolve('p1')},1000);
})

let p2 = new Promise((resolve, reject) = > {
  setTimeout((a)= > {
    resolve('p2')},2000);
})

// Prmose.all 
console.time('cost')
Promise.all([p1, p2]).then(data= > {
  console.log(data);
  console.timeEnd('cost')})Copy the code

The output is

[ 'p1'.'p2' ]
cost: 2026.419ms
Copy the code

Code implementation

The use of the call is through promise.all, indicating that the method is on the class, not the instance, so static is used instead

class Promise{
  / /...
  static all(proArr) {
    return new Promise((resolve, reject) = > {
      // ...}}})Copy the code

What we end up returning is an array of the values returned by each promise in proArr, stored in the order promised

So we set an array ret = [] to cache the data

We iterate through proArr to implement the promise in it

static all(proArr) {
  return new Promise((resolve, reject) = > {
    let ret = []
    let done = (i, data) = > {
      ret[i] = data
    }
    for (let i = 0; i < proArr.length; i++) {
      proArr[i].then(data= > done(i,data) , reject)
    }
  })
}
Copy the code

Here we need a helper function, done, to do the saving for each successful promise return

Then why not just be direct

proArr[i].then(data= > ret[i] = data, reject)
Copy the code

Because we also need a variable to tell us if the RET is full

When it’s full, we send resolve(ret)

static all(proArr) {
  return new Promise((resolve, reject) = > {
    let ret = []
    let count = 0
    let done = (i, data) = > {
      ret[i] = data
      if(++count === proArr.length) resolve(ret)
    }
    for (let i = 0; i < proArr.length; i++) {
      proArr[i].then(data= > done(i,data) , reject)
    }
  })
}
Copy the code

Why use a count variable, count? So let’s write it the wrong way without using count

Easy wrong points

This is where bugs come in

static all(proArr) {
  return new Promise((resolve, reject) = > {
    let ret = []
    let done = (i, data) = > {
      ret[i] = data
      if(i === proArr.length - 1) resolve(ret)
    }
    for (let i = 0; i < proArr.length; i++) {
      proArr[i].then(data= > done(i,data) , reject)
    }
  })
}
Copy the code

We use I equal to proArr. Length-1 as the trigger condition when the data is full

It looks like once we’ve executed the last promise of proArr we’ll execute resolve(ret)

This is because you don’t understand what promise. then means, and then methods bind methods to promises

The bound method is not executed immediately, but after the then promise succeeds or fails

So if we write it this way, if proArr[0] takes 5s to complete, that is, 5 seconds to execute

done = (0, data) = > {
  ret[0] = data
  if(i === proArr.length - 1) resolve(ret)
}
Copy the code

So proArr[2] is done in 1 second, so it’s executed in 1 second

done = (2, data) = > {
  ret[2] = data
  if(i === proArr.length - 1) resolve(ret) 
}
Copy the code

Resolve (ret) proArr. Length -1 = 2

However, we know that proArr[0] has not yet been executed, i.e. we have not yet added data to RET [0]

That’s why count is used, ++count is executed when any of the proArr data is filled in

Resolve (ret) if ++count === proarr. length

Currie,

So the requirements above are pretty good for currie

static all(proArr) {
  return new Promise((resolve, reject) = > {
    let done = (function (len, cb) {
      const ret = []
      let count = 0
      return function (i, data) {
        ret[i] = data
        if (++count === len) cb(ret)
      }
    })(proArr.length, resolve)

    for (let i = 0; i < proArr.length; i++) {
      proArr[i].then(data= > done(i, data), reject)
    }
  })
}
Copy the code

Of course it looks the same, of course, because if we write it this way, we can move the function out, and then the logic is clear

Promise{
  // ...
  static all(proArr) {
    return new Promise((resolve, reject) = > {
      let done = currying(proArr.length, resolve)
      for (let i = 0; i < proArr.length; i++) {
        proArr[i].then(data= > done(i, data), reject)
      }
    })
  }
}

function currying(len, cb) {
  const ret = []
  let count = 0
  return function (i, data) {
    ret[i] = data
    if (++count === len) cb(ret)
  }
}
Copy the code

Promise.race

Like all, it accepts an array of promise objects.

The difference is that the result returns the promise data that is returned first, which in this case is the single data that is returned first. It’s kind of like a competition. Whoever gets there first gets hired.

let p1 = new Promise((resolve, reject) = > {
  setTimeout((a)= > {
    resolve('p1')},1000);
})

let p2 = new Promise((resolve, reject) = > {
  setTimeout((a)= > {
    resolve('p2')},2000);
})

// Promise.race
console.time('cost')
Promise.race([p1, p2]).then(data= > {
	console.log(data);
	console.timeEnd('cost')})Copy the code

The output is

p1
cost: 1014.655ms
Copy the code

When the called interface is unstable, we can fetch more than one, and use whichever one we get first.

We can also use it as a timer for promise, and if we don’t meet the deadline, we do a Reject

let p2 = new Promise((resolve, reject) = > {
  setTimeout((a)= > {
    resolve('p2')},2000);
})

function tiemout(delay) {
  return new Promise((resolve, reject) = > {
    setTimeout((a)= > {
      reject('tiemout') }, delay); })}Promise.race([p2, tiemout(500)]).then(res= > {
  console.log(res);
}).catch(err= > {
  console.log(err);
})
Copy the code

If p2 does not receive data after 500ms, timout executes reject and timeout

And that’s easy to implement

static race(promiseAry) {
  return new Promise((resolve, reject) = > {
    for (let i = 0; i < promiseAry.length; i++) {
      promiseAry[i].then(resolve, reject)
    }
  })
}
Copy the code

The promise state can only be changed once, that is, resolve and Reject can only be implemented once. We put resolve and Reject methods in promie into the success or failure callback of each promise in the promiseAry. Resolve and Reject are called when either of them succeeds or fails, and once called, they are never called again.

catch && resolve && reject

catch(onRejected) {
  return this.then(null, onRejected)
}
static resolve(value) {
  return new Promise((resolve, reject) = > resolve(value))
}

static reject(reason) {
  return new Promise((resolve, reject) = > reject(reason))
}
Copy the code

So the above implementation is pretty simple, there’s nothing to talk about, there’s a catch, right

When we write “then”, we have two parameters, one success and one failure, so it is easy to confuse, so it is a practice to write only the success function in the “then” method, and then add a catch to handle the failure

Promise.reject('failure')
  .then(data= > { console.log(data) })
  .catch(reason= > { console.log(reason) })
Copy the code

If rejected is not a function, it will throw the error back.

The source code

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('Circular reference'));
  }
  let then
  let called = false
  if (x instanceof Promise) {
    if (x.status == PENDING) {
      x.then(
        y= > resolvePromise(promise2, y, resolve, reject),
        r => reject(r)
      )
    } else x.then(resolve, reject);
  } else if(x ! =null && ((typeof x == 'object' || typeof x == 'function'))) {
    try {
      then = x.then;
      if (typeof then == 'function') {
        then.call(
          x,
          y => {
            // Prevent promise from executing both successful and failed callbacks
            // If promise2 has already succeeded or failed, it is no longer processed
            if (called) return;
            called = true;
            resolvePromise(promise2, y, resolve, reject);
          },
          r => {
            // Prevent promise from executing both successful and failed callbacks
            // If promise2 has already succeeded or failed, it is no longer processed
            if (called) return;
            called = true;
            reject(r);
          });
      } else{ resolve(x); }}catch (e) {
      if (called) return;
      called = true; reject(e); }}else{ resolve(x); }}// function gen(times, cb) {
// let ret = []
// let count = 0
// return function (i, data) {
// ret[i] = data
// if (++count === times) {
// cb(ret)
/ /}
/ /}
// }
class Promise {
  constructor(executor) {
    // Set the state
    this.status = PENDING
    this.value = undefined
    // Define the callback array to execute after the store succeeds
    this.onResolvedCallbacks = []
    // Define an array of callbacks to execute after a failure
    this.onRejectedCallbacks = []

    let resolve = data= > {
      let timer = setTimeout((a)= > {
        clearTimeout(timer)
        if (this.status === PENDING) {
          this.status = FULFILLED
          this.value = data
          this.onResolvedCallbacks.forEach(cb= > cb(this.value))
        }
      })
      }
    let reject = reason= > {
      let timer = setTimeout((a)= > {
        clearTimeout(timer)
        if (this.status === PENDING) {
          this.status = REJECTED
          this.value = reason
          this.onRejectedCallbacks.forEach(cb= > cb(this.value))
        }
      })
      }

    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value= > value;
    onRejected = typeof onRejected == 'function' ? onRejected : reason= > { throw reason };
    let promise2

    if (this.status === FULFILLED) {
      promise2 = new Promise((resolve, reject) = > {
        let timer = setTimeout((a)= > {
          clearTimeout(timer)
          try {
            let x = onFulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
        })
    }
    if (this.status === REJECTED) {
      promise2 = new Promise((resolve, reject) = > {
        let timer = setTimeout((a)= > {
          clearTimeout(timer)
          try {
            let x = onRejected(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
        })
    }
    if (this.status === PENDING) {
      promise2 = new Promise((resolve, reject) = > {
        this.onResolvedCallbacks.push(value= > {
          try {
            let x = onFulfilled(value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
        this.onRejectedCallbacks.push(reason= > {
          try {
            let x = onRejected(reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      })
    }

    return promise2
  };

  catch(onRejected) {
    return this.then(null, onRejected)
  }
  static resolve(value) {
    return new Promise((resolve, reject) = > resolve(value))
  }

  static reject(reason) {
    return new Promise((resolve, reject) = > reject(reason))
  }

  // static all(promiseAry) {
  // return new Promise((resolve, reject) => {
  // let done = gen(promiseAry.length, resolve)
  // for (let i = 0; i < promiseAry.length; i++) {
  // promiseAry[i].then(
  // data => { done(i, data) },
  // reject)
  / /}
  / /})
  // }
  static all(promiseAry) {
    return new Promise((resolve, reject) = > {
      let ret = []
      let count = 0
      for (let i = 0; i < promiseAry.length; i++) {
        promiseAry[i].then(
          data= > {
            ret[i] = data
            if (++count === promiseAry.length) {
              resolve(ret)
            }
          },
          reason => reject(reason)
        )
      }
    })
  }

  static race(promiseAry) {
    return new Promise((resolve, reject) = > {
      for (let i = 0; i < promiseAry.length; i++) {
        promiseAry[i].then(resolve, reject)
      }
    })
  }
}





/ / test
Promise.deferred = Promise.defer = function () {
  var defer = {};
  defer.promise = new Promise(function (resolve, reject) {
    defer.resolve = resolve;
    defer.reject = reject;
  })
  return defer;
}
try {
  module.exports = Promise
} catch (e) {
}
Copy the code