首页 > 解决方案 > 如果未传递可选回调,如何使函数返回承诺?

问题描述

我可以想出

 function squareAsync(val, callback) {
if (callback) {
    setTimeout(() => {
        if (Math.random() < 0.5) {
            callback(undefined, val * val);
        }
        else {
            callback(new Error('Failed!'));
        }
    }, 2000);
}
else {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (Math.random() < 0.5) {
                resolve(val * val);
            }
            else {
                reject(new Error('Failed!'));
            }
        }, 2000);
   });
 }
}

我为此找到了另一种方法

 function squareAsync1(val, callback) {
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        if (Math.random() < 0.5) {
            resolve(val * val);
        }
        else {
            reject(new Error('Failed!'));
        }
    }, 2000);
});
if (callback) {
    p.then(d => {
        callback(undefined, d);
    }, e => {
        callback(e);
    });
 }
return p;
}

其中哪一个更好,或者有更标准和更优雅的方式来做到这一点?我们可以这样做async/await吗?

标签: javascript

解决方案


你可以这样做:

function squareAsync(val, callback) {
  const timeout = function(res, rej){
    setTimeout(function(){
      if (Math.random() < 0.5)
        res(val*val);
      else
        rej(new Error('Failed!'));
    }, 2000);
  }
  
  return typeof callback === 'function'
    ? timeout(callback.bind(null, undefined), callback)
    : new Promise(timeout);
}

// CALLBACK EXAMPLE
squareAsync(5, (err, val) => {
  if (err)
    console.log(`Callback: ${err}`);
  else
    console.log(`Callback: ${val}`);
})

// PROMISE EXAMPLE
squareAsync(5)
  .then(val => console.log(`Promise: ${val}`))
  .catch(err => console.log(`Promise: ${err}`))

解释

  1. 将您的setTimeout调用包装到一个包装函数timeout中,这样您就不必重复几乎相同的代码。
  2. timeout函数接受两个参数:resand rej (resolve and reject)
  3. timeout如果回调是通过函数传递的,则返回,否则返回new Promise(timeout)

现在关于发生了什么:

return typeof callback === 'function'
  ? timeout(callback.bind(null, undefined), callback)
  : new Promise(timeout);

它转化为:

if (typeof callback === 'function'){
  // Bind `null` as `this` value to `callback
  // and `undefined` as its first argument (because no error).
  // Need to to this because in `timeout` function,
  // we call `res` with only 1 argument (computed value) if success.
  const resolve = callback.bind(null, undefined);

  // Don't need to bind anything
  // because the first argument should be error.
  const reject = callback;

  // Call the function as if we are in a Promise
  return timeout(resolve, reject);
}

// Use `timeout` function as normal promise callback.
return new Promise(timeout);

希望你能理解。如果感到困惑,请随时发表评论。

更多关于绑定。


推荐阅读