Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【Q022】如何实现一个简单的 Promise #23

Open
shfshanyue opened this issue Nov 10, 2019 · 5 comments
Open

【Q022】如何实现一个简单的 Promise #23

shfshanyue opened this issue Nov 10, 2019 · 5 comments

Comments

@shfshanyue
Copy link
Owner

No description provided.

@shfshanyue shfshanyue added the js label Nov 10, 2019
@shfshanyue
Copy link
Owner Author

shfshanyue commented Nov 13, 2019

一个简单的 Promise 的粗糙实现,关键点在于

  1. pending 时, thenable 函数由一个队列维护
  2. 当状态变为 resolved(fulfilled) 时,队列中所有 thenable 函数执行
  3. resolved 时, thenable 函数直接执行

rejected 状态同理

class Prom {
  static resolve (value) {
    if (value && value.then) {
      return value 
    }
    return new Prom(resolve => resolve(value))
  }

  constructor (fn) {
    this.value = undefined
    this.reason = undefined
    this.status = 'PENDING'

    // 维护一个 resolve/pending 的函数队列
    this.resolveFns = []
    this.rejectFns = []

    const resolve = (value) => {
      // 注意此处的 setTimeout
      setTimeout(() => {
        this.status = 'RESOLVED'
        this.value = value
        this.resolveFns.forEach(({ fn, resolve: res, reject: rej }) => res(fn(value)))
      })
    }

    const reject = (e) => {
      setTimeout(() => {
        this.status = 'REJECTED'
        this.reason = e
        this.rejectFns.forEach(({ fn, resolve: res, reject: rej }) => rej(fn(e)))
      })
    }

    fn(resolve, reject)
  }


  then (fn) {
    if (this.status === 'RESOLVED') {
      const result = fn(this.value)
      // 需要返回一个 Promise
      // 如果状态为 resolved,直接执行
      return Prom.resolve(result)
    }
    if (this.status === 'PENDING') {
      // 也是返回一个 Promise
      return new Prom((resolve, reject) => {
        // 推进队列中,resolved 后统一执行
        this.resolveFns.push({ fn, resolve, reject }) 
      })
    }
  }

  catch (fn) {
    if (this.status === 'REJECTED') {
      const result = fn(this.value)
      return Prom.resolve(result)
    }
    if (this.status === 'PENDING') {
      return new Prom((resolve, reject) => {
        this.rejectFns.push({ fn, resolve, reject }) 
      })
    }
  }
}

Prom.resolve(10).then(o => o * 10).then(o => o + 10).then(o => {
  console.log(o)
})

return new Prom((resolve, reject) => reject('Error')).catch(e => {
  console.log('Error', e)
})

@shfshanyue shfshanyue added the code label Jun 4, 2021
@heretic-G
Copy link

function MyPromise (executor) {
    if (typeof executor !== 'function') {
        // throw new Error('Promise resolver 1 is not a function')
    }
    if (this instanceof MyPromise) {
        // throw new Error(`${this} is not a promise`)
    }
    this.PromiseState = 'pending'
    this.PromiseFulfillReactions = []
    this.PromiseRejectReactions = []
    this.PromiseIsHandled = false
    this.AlreadyResolved = false

    let resolve = _Resolve(this)
    let reject = _Reject(this)

    try {
        executor(resolve, reject)
    } catch (e) {
        reject(e)
    }
}

MyPromise.prototype.then = function (onFulfilled, onRejected) {
    let promise = this
    let capability = NewPromiseCapability()
    return PerformPromiseThen(promise, onFulfilled, onRejected, capability)
}

function _Resolve (promise) {
    return function __Resolve (resolution) {
        if (promise.AlreadyResolved) {
            return undefined
        }
        promise.AlreadyResolved = true
        if (resolution === promise) {
            return RejectPromise(promise, TypeError('is same'))
        }
        if ((typeof resolution !== 'function' && typeof resolution !== 'object') || resolution === null) {
            return FulfillPromise(promise, resolution)
        }
        let then
        try {
            then = resolution.then
        } catch (e) {
            return RejectPromise(promise, e)
        }
        if (typeof then !== 'function') {
            return FulfillPromise(promise, resolution)
        } else {
            let job = NewPromiseResolveThenableJob(promise, resolution, then)
            HostEnqueuePromiseJob(job)
        }
        return undefined
    }
}

function _Reject (promise) {
    return function __Reject (reason) {
        if (promise.AlreadyResolved) {
            return undefined
        }
        promise.AlreadyResolved = true
        RejectPromise(promise, reason)
    }
}

function executor (resolve, reject) {
    this.resolve = resolve
    this.reject = reject
}

function NewPromiseCapability () {
    let capability = {
        resolve: undefined,
        reject: undefined,
        promise: undefined
    }
    capability.promise = new MyPromise(executor.bind(capability))
    return capability
}

function PerformPromiseThen (promise, onFulfilled, onRejected, resultCapability) {
    let fulfillReaction = {
        Capability: resultCapability,
        Type: 'Fulfill',
        Handler: onFulfilled
    }
    let rejectReaction = {
        Capability: resultCapability,
        Type: 'Reject',
        Handler: onRejected
    }
    if (promise.PromiseState === 'pending') {
        promise.PromiseFulfillReactions.push(fulfillReaction)
        promise.PromiseRejectReactions.push(rejectReaction)
    } else if (promise.PromiseState === 'fulfilled') {
        let resolution = promise.PromiseResult
        let job = NewPromiseReactionJob(fulfillReaction, resolution)
        HostEnqueuePromiseJob(job)
    } else {
        if (!promise.PromiseIsHandled) {

        }
        let reason = promise.PromiseResult
        let job = NewPromiseReactionJob(rejectReaction, reason)
        HostEnqueuePromiseJob(job)
    }
    promise.PromiseIsHandled = true
    if (!resultCapability) return undefined
    return resultCapability.promise
}

function FulfillPromise (promise, resolution) {
    if (promise.PromiseState !== 'pending') {
        return undefined
    }
    let reactions = promise.PromiseFulfillReactions
    promise.PromiseResult = resolution
    promise.PromiseRejectReactions = []
    promise.PromiseFulfillReactions = []
    promise.PromiseState = 'fulfilled'
    TriggerPromiseReactions(reactions, resolution)
}

function RejectPromise (promise, reason) {
    if (promise.PromiseState !== 'pending') {
        return undefined
    }
    let reactions = promise.PromiseRejectReactions
    promise.PromiseResult = reason
    promise.PromiseRejectReactions = []
    promise.PromiseFulfillReactions = []
    promise.PromiseState = 'rejected'
    if (!promise.PromiseIsHandled) {

    }
    TriggerPromiseReactions(reactions, reason)
}

function TriggerPromiseReactions (reactions, argument) {
    reactions.forEach(curr => {
        let job = NewPromiseReactionJob(curr, argument)
        HostEnqueuePromiseJob(job)
    })
}

function NewPromiseReactionJob (reaction, argument) {
    return function () {
        let capability = reaction.Capability
        let type = reaction.Type
        let handler = reaction.Handler
        let handlerResult
        let isError = false
        if (typeof handler !== 'function') {
            if (type === 'Fulfill') {
                handlerResult = argument
            } else {
                isError = true
                handlerResult = argument
            }
        } else {
            try {
                handlerResult = handler(argument)
            } catch (e) {
                isError = true
                handlerResult = e
            }
        }
        if (!capability) return undefined
        let status
        if (!isError) {
            status = capability.resolve(handlerResult)
        } else {
            status = capability.reject(handlerResult)
        }
        return status
    }
}

function NewPromiseResolveThenableJob (promiseToResolve, thenable, then) {
    return function () {
        let resolve = _Resolve(promiseToResolve)
        let reject = _Reject(promiseToResolve)
        promiseToResolve.AlreadyResolved = false
        let result
        try {
            result = then.call(thenable, resolve, reject)
        } catch (e) {
            return reject(e)
        }
        return result
    }
}

function HostEnqueuePromiseJob (job) {
    setTimeout(job, 0)
}


MyPromise.deferred = function () {
    let dfd = {};
    dfd.promise = new MyPromise((resolve,reject)=>{
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}

module.exports = MyPromise

@hsq777
Copy link

hsq777 commented Apr 8, 2022

一个简单的 Promise 的粗糙实现,关键点在于

  1. pending 时, thenable 函数由一个队列维护
  2. 当状态变为 resolved(fulfilled) 时,队列中所有 thenable 函数执行
  3. resolved 时, thenable 函数直接执行

rejected 状态同理

class Prom {
  static resolve (value) {
    if (value && value.then) {
      return value 
    }
    return new Prom(resolve => resolve(value))
  }

  constructor (fn) {
    this.value = undefined
    this.reason = undefined
    this.status = 'PENDING'

    // 维护一个 resolve/pending 的函数队列
    this.resolveFns = []
    this.rejectFns = []

    const resolve = (value) => {
      // 注意此处的 setTimeout
      setTimeout(() => {
        this.status = 'RESOLVED'
        this.value = value
        this.resolveFns.forEach(({ fn, resolve: res, reject: rej }) => res(fn(value)))
      })
    }

    const reject = (e) => {
      setTimeout(() => {
        this.status = 'REJECTED'
        this.reason = e
        this.rejectFns.forEach(({ fn, resolve: res, reject: rej }) => rej(fn(e)))
      })
    }

    fn(resolve, reject)
  }


  then (fn) {
    if (this.status === 'RESOLVED') {
      const result = fn(this.value)
      // 需要返回一个 Promise
      // 如果状态为 resolved,直接执行
      return Prom.resolve(result)
    }
    if (this.status === 'PENDING') {
      // 也是返回一个 Promise
      return new Prom((resolve, reject) => {
        // 推进队列中,resolved 后统一执行
        this.resolveFns.push({ fn, resolve, reject }) 
      })
    }
  }

  catch (fn) {
    if (this.status === 'REJECTED') {
      const result = fn(this.value)
      return Prom.resolve(result)
    }
    if (this.status === 'PENDING') {
      return new Prom((resolve, reject) => {
        this.rejectFns.push({ fn, resolve, reject }) 
      })
    }
  }
}

Prom.resolve(10).then(o => o * 10).then(o => o + 10).then(o => {
  console.log(o)
})

return new Prom((resolve, reject) => reject('Error')).catch(e => {
  console.log('Error', e)
})

catch里面应该是return Prom.reject(result)

@hefeng6500
Copy link

话不多说,看码!

// 1、基本架构:
//   状态
//   then
//   执行器函数 executor

// 2、executor、resolve、reject
// 3、then 同步下调用
// 4、then 异步下调用
// 5、then 链式调用
//   返回 Promise
//   then 函数递归返回常量结果,供下个 then 使用
//   考虑 then 成功的回调为 null 的情况

class Promise {
  static PENDING = 'pending';
  static RESOLVED = 'resolved';
  static REJECTED = 'rejected';

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

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

  constructor(executor) {
    this.state = Promise.PENDING;
    this.value = undefined;
    this.reason = undefined;

    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = value => {
      if (value instanceof Promise) {
        return value.then(resolve, reject);
      }

      if (this.state === Promise.PENDING) {
        this.state = Promise.RESOLVED;
        this.value = value;

        this.onResolvedCallbacks.forEach(fn => fn());
      }
    };
    const reject = reason => {
      this.state = Promise.REJECTED;
      this.reason = reason;
      this.onRejectedCallbacks.forEach(fn => fn());
    };

    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 promise = new Promise((resolve, reject) => {
      if (this.state === Promise.PENDING) {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });
      }

      if (this.state === Promise.RESOLVED) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      }

      if (this.state === Promise.REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      }
    });

    return promise;
  }

  catch(onRejected) {
    return this.then(null, onRejected);
  }

  all(arr) {
    let count = 0;
    let result = [];

    return new Promise((resolve, reject) => {
      for (let i = 0; i < arr.length; i++) {
        Promise.resolve(arr[i])
          .then(res => {
            result[i] = res;
            if (++count === arr.length) {
              resolve(res);
            }
          })
          .catch(error => {
            reject(error);
          });
      }
    });
  }

  race(arr) {
    return new Promise((resolve, reject) => {
      arr.forEach(item => Promise.resolve(item).then(resolve, reject));
    });
  }

  finally(callback) {
    return this.then(
      value => {
        return Promise.resolve(callback()).then(() => value);
      },
      reason => {
        return Promise.resolve(callback()).then(() => {
          throw reason;
        });
      },
    );
  }

  allSettled(arr) {
    let count = 0;
    let result = [];

    return new Promise((resolve, reject) => {
      const fn = (i, data) => {
        if (count === arr.length) {
          resolve(result);
        }

        result[i] = data;
        count++;
      };

      for (let i = 0; i < arr.length; i++) {
        Promise.resolve(arr[i])
          .then(res => {
            fn(i, { status: 'fulfilled', value: res });
          })
          .catch(error => {
            fn(i, { status: 'rejected', reason: error });
          });
      }
    });
  }

  // from Node Util.promisify
  promisify(f) {
    return function(...args) {
      return new Promise((resolve, reject) => {
        function callback(error, result) {
          if (error) {
            reject(error);
          } else {
            resolve(result);
          }
        }

        args.push(callback);

        f.call(this, ...args);
      });
    };
  }

  // from Node Util.promisifyAll
  promisifyAll(obj) {
    for (let key in obj) {
      if (typeof obj[key] === 'function') {
        obj[key] = this.promisify(obj[key]);
      }
    }
  }
}

function resolvePromise(promise, x, resolve, reject) {
  // let promise = new Promise((resolve) => {
  //   resolve(1);
  // }).then((res) => {
  //   return promise;
  // });

  if (x === promise) {
    throw TypeError('循环引用');
  }

  if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
    let called;

    try {
      let then = x.then;

      if (typeof then === 'function') {
        then.call(
          x,
          y => {
            if (called) return;
            called = true;
            resolvePromise(promise, y, resolve, reject);
          },
          r => {
            if (called) return;
            called = true;
            reject(r);
          },
        );
      } else {
        // x: { then: {} }
        if (called) return;
        called = true;
        resolve(x);
      }
    } catch (error) {
      if (called) return;
      called = true;
      reject(error);
    }
  } else {
    // 返回了常量,直接 resolve
    resolve(x);
  }
}

const p = new Promise((resolve, reject) => {
  reject(1);
});

p.catch(error => {
  console.log('error + ', error);
  return error;
}).then(res => {
  console.log(res);
});

Promise.deferred = function() {
  let dfd = {};
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
};

module.exports = Promise;

@Yu-Lxy
Copy link

Yu-Lxy commented Jun 21, 2022

在作者代码基础上做了一些修改
基本功能够用了

class myPromise {
    static PENDING = 'pending'
    static FULFILLED = 'fulfilled'
    static REJECTED = 'rejected'

    static resolve (value) {
        if (value && value.then) {
            return value
        }
        return new myPromise((resolve) => resolve(value))
    }

    static reject (value) {
        return new myPromise((_, reject) => reject(value))
    }

    constructor (fn) {
        this.status = myPromise.PENDING
        this.result = null

        this.resFns = []
        this.rejFns = []

        const resolve = (value) => {
            if (this.status === myPromise.PENDING) {
                setTimeout(() => {
                    this.status = myPromise.FULFILLED
                    this.result = value
                    this.resFns.forEach(({fn, resolve, reject}) => resolve(fn(value)))
                })
            }
        }

        const reject = (reason) => {
            if (this.status === myPromise.PENDING) {
                setTimeout(() => {
                    this.status = myPromise.REJECTED
                    this.result = reason
                    this.rejFns.forEach(({fn, resolve, reject}) => reject(fn(reason)))
                })
            }
        }

        try {
            fn(resolve, reject)
        } catch (err) {
            reject(err)
        }
    }

    then (resFn, rejFn) {
        resFn = typeof resFn === 'function' ? resFn : value => value
        rejFn = typeof rejFn === 'function' ? rejFn : reason => reason

        const _promise = {
            [myPromise.PENDING]: () => {
                return new myPromise((resolve, reject) => {
                    this.resFns.push({fn: resFn, resolve, reject})
                    this.rejFns.push({fn: rejFn, resolve, reject})
                })
            },
            [myPromise.FULFILLED]: () => myPromise.resolve(resFn(this.result)),
            [myPromise.REJECTED]: () => myPromise.reject(rejFn(this.result))
        }[this.status]

        return _promise()
    }

    catch (fn) {
        return this.then(undefined, fn)
    }

    finally(cb) {
        return this.then(cb, cb)
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants