Promise的总结与使用

promise的使用总结

promise的详细语法请参考MDN 我们知道,一个promise有3种状态:pending(初始状态,既不是成功,也不是失败状态)、fulfilled(意味着操作成功完成)、rejected(味着操作失败)。

new Promise( function(resolve, reject) {...} /* executor */  );

fullfilled完成状态

调用resolve函数会将promise的状态改为fullfilled完成状态:

let promise = new Promise((resolve, reject) => {
  setTimeout(()=> {
    resolve('我被调用了')
  },200)
})
promise.then((result) => {
  console.log(result)
}).catch(error => {
  console.log(error)
}).finally(() => {
  console.log('我最终才会被执行');
})
//我被调用了
//我最终才会被执行

我们还可以使用Promise的静态方法Promise.resolve(value)来返回一个Promise:

let promise = Promise.resolve('我是resolve');
promise.then(result => {
  console.log(result)
})
//我是resolve

//也可以是链式的写法
Promise.resolve("Success").then(function(value) {
  console.log(value); // "Success"
});

resolve另一个promise

let original = Promise.resolve(313);
let cast = Promise.resolve(original);
cast.then(value => {
  console.log('value: ' + value);
});
console.log('original === cast ? ' + (original === cast));
//打印顺序如下,这里有一个同步异步先后执行的区别
//original === cast ? true
//value: 313

日志顺序颠倒其实是由于异步地调用then 方法.至于then()方法,在后面我们会说到。

rejected失败状态

调用reject函数会将promise的状态改为rejected失败状态:

let promise = new Promise((resolve, reject) => {
  setTimeout(()=> {
    reject('我被调用了')
  },200)
})
promise.then((result) => {
  console.log(result)
}).catch(error => {
  console.log('我发生了错误')
  console.log(error)
}).finally(() => {
  console.log('我最终才会被执行');
})

//我发生了错误
// 我被调用了
// 我最终才会被执行

同样的,我们还可以使用Promise的静态方法Promise.reject(reason)来返回一个被拒绝的Promise对象:

Promise.reject('糟糕!发生了错误').then(res => {
  console.log('我不会被调用');
}, reason => {
  console.log('我被调用了:', reason);
})
//我被调用了: 糟糕!发生了错误

then()方法的使用

then() 方法返回一个 Promise。它最多需要有两个参数:Promise 的成功情况resolve和失败情况reject的回调函数,它的语法如下:

p.then(onFulfilled[, onRejected]);

p.then(value => {
  // fulfillment
}, reason => {
  // rejection
});

成功状态:


let promise = new Promise((resolve, reject) => {
  setTimeout(()=> {
    resolve('我是成功的回调')
  },200)
})
promise.then((res) => {
  console.log(res) //我是成功的回调
}, (error) => {
  console.log(error) 
})

失败状态:

let promise = new Promise((resolve, reject) => {
  setTimeout(()=> {
    reject('我是失败的回调')
  },200)
})
promise.then((res) => {
  console.log(res) 
}, (error) => {
  console.log(error) //我是失败的回调
})

then的回调时机:

const resolvedProm = Promise.resolve(313);

let thenProm = resolvedProm.then(value => {
  console.log("当前线程循环结束前调用: " + value);
  return value;
});
console.log('调用了:', thenProm);

setTimeout(() => {
  console.log('setTimeout的回调:',thenProm);
});
/**
 * 调用了: Promise {<pending>}
 * 当前线程循环结束前调用: 313
 * setTimeout的回调: Promise {<resolved>: 313}
 */

then方法的链式调用

then 方法返回一个 Promise 对象,那么这就可以使用链式调用了:

Promise.resolve('first').then(result => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      result += '---->second'
      resolve(result)
    }, 2000)
  })
}).then(result => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      result += '---->third'
      resolve(result)
    }, 2000)
  })
}).then(result => {
  console.log('最终的结果是:',result); //最终的结果是: first---->second---->third
})

当一个值只是从一个 then 内部返回时,它将等价地返回 Promise.resolve(<由被调用的处理程序返回的值>):

Promise.resolve('first').then(result => {
  return result +  '---->second';
}).then(result => {
  return result + '---->third';
}).then(result => {
  console.log('最终的结果是:', result); 
});
console.log('调用了');

/**
 * 调用了
 * 最终的结果是: first---->second---->third
 */

注意下面的打印顺序,’哈哈哈,我也调用了: 2’是最后被调用的:

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

p2.then(function(value) {
  console.log('调用了:', value);
  return value + 1;
}).then(function(value) {
  console.log('哈哈哈,我也调用了:', value);
});

p2.then(function(value) {
  console.log('哦哦哦,看来我也调用了:', value);
});
p2.then(function(value) {
  console.log('啊啊啊,我也调用了:', value);
});
/**
 * 调用了: 1
 * 哦哦哦,看来我也调用了: 1
 * 啊啊啊,我也调用了: 1
 * 哈哈哈,我也调用了: 2
 */

在catch回调之后,还能继续then的回调:

Promise.resolve()
  .then(() => {
    // 使 .then() 返回一个 rejected promise
    throw new Error('发生了一个错误!');
  })
  .catch(error => {
    console.error('onRejected被回调了: ' + error.message);
  })
  .then(() => {
    console.log("即便reject被回调了,我还是能被调用...");
  });
/**
 *  onRejected被回调了: 发生了一个错误!
 *  即便reject被回调了,我还是能被调用...
 */

Promise.all(iterable)方法

Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数可以是promise对象,也可以是普通的值,我们可以封装iterable为一个数组。如果iterable中包含promise对象,并且 promise有一个失败(rejected),此实例的回调就会失败(reject),失败原因的是第一个失败 promise 的结果。

全部都是promise对象:

var promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('我是promise1, 我被调用了')
  },2000)
})
var promise2 = Promise.resolve('我是promise2, 我被调用了')
var promise3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('我是promise3, 我被调用了')
  },1200)
})
var promise4 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('我是promise4, 我被调用了')
  },4000)
})

Promise.all([promise1, promise2, promise3, promise4]).then(result => {
  console.log(result)
})
// ["我是promise1, 我被调用了", "我是promise2, 我被调用了", "我是promise3, 我被调用了", "我是promise4, 我被调用了"]

数组中可以传入非promise数据:

tips:如果参数中包含非 promise 值,这些值将被忽略,但仍然会被放在返回数组中(如果 promise 完成的话)

let promise1 = Promise.resolve('我是promise1, 我被调用了')
let name = 'LXX'
let age = 18
Promise.all([promise1, name, age]).then(result => {
  console.log(result)
})
// ["我是promise1, 我被调用了", "LXX", 18]

注意:返回值将会按照参数内的 promise 顺序排列,而不是由调用 promise 的完成顺序决定。
如果传入的 promise 中有失败(rejected):

let promise1 = Promise.resolve('我是promise1, 我被调用了')
let name = 'LXX'
let age = 18
let promise2 = Promise.resolve('我是promise2, 我被调用了')
let promise3 = Promise.reject('我是promise3, 我被拒绝了')
let promise4 = Promise.resolve('我是promise4, 我被调用了')
Promise.all([promise1, name, age, promise2, promise3, promise4]).then(result => {
  console.log(result)
})
// Uncaught (in promise) 我是promise3, 我被拒绝了

如果传入的 promise 中有一个失败(rejected),Promise.all 异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise 是否完成

异步性
let promise1 = Promise.resolve('我是promise1, 我被调用了')
let promise2 = Promise.resolve('我是promise2, 我被调用了')
let p = Promise.all([promise1, promise2])
console.log('我的状态是:');
console.log(p);
p.then((result) => {
  console.log('我在then的回调:');
  console.log(result)
  console.log(p);
})
setTimeout(() => {
  console.log('我在setTimeout的回调:');
  console.log(p);
})
console.log('我被执行了');

//我的状态是:
//Promise {<pending>}

//我被执行了

//我在then的回调:
//(2) ["我是promise1, 我被调用了", "我是promise2, 我被调用了"]
//Promise {<resolved>: Array(2)}

//我在setTimeout的回调:
//Promise {<resolved>: Array(2)}
let promise1 = Promise.resolve('我是promise1, 我被调用了')
let promise2 = Promise.resolve('我是promise2, 我被调用了')
let p = Promise.all([promise1, promise2])
console.log('我的状态是:');
console.log(p);

setTimeout(() => {
  console.log('我在setTimeout的回调:');
  console.log(p);
})

p.then((result) => {
  console.log('我在then的回调:');
  console.log(result)
  console.log(p);
})

console.log('我被执行了');

//我的状态是:
//Promise {<pending>}

//我被执行了

//我在then的回调:
//(2) ["我是promise1, 我被调用了", "我是promise2, 我被调用了"]
//Promise {<resolved>: Array(2)}

//我在setTimeout的回调:
//Promise {<resolved>: Array(2)}

由于我们传入的是resolve的promise数组,所以会触发promise.all。我们发现,无论setTimeout是在p.then前还是后面,都是最后调用。

Promise.race(iterable)的用法

race 函数返回一个 Promise,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个。

var p1 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 500, "one"); 
});
var p2 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 100, "two"); 
});

Promise.race([p1, p2]).then(function(value) {
  console.log(value); // "two"
  // 两个都完成,但 p2 更快
});

var p3 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 100, "three");
});
var p4 = new Promise(function(resolve, reject) { 
    setTimeout(reject, 500, "four"); 
});

Promise.race([p3, p4]).then(function(value) {
  console.log(value); // "three"
  // p3 更快,所以它完成了              
}, function(reason) {
  // 未被调用
});

var p5 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 500, "five"); 
});
var p6 = new Promise(function(resolve, reject) { 
    setTimeout(reject, 100, "six");
});

Promise.race([p5, p6]).then(function(value) {
  // 未被调用             
}, function(reason) {
  console.log(reason); // "six"
  // p6 更快,所以它失败了
});

举例

使用Promise.all的场景举例

在uni-app中,有一个复杂的页面,需要多个接口返回数据,我们希望在多个接口数据未全部返回数据时使用loading,全部成功后结束loading。多个接口中如果有一个返回异常报错,则提示用户数据异常。这种场景下,我们就可以使用promise了。

image

如上图,我们需要同时请求多个接口,接口之间并没有数据依赖,那么就可以使用Promise.all了。

下面是简化后的代码:

        onLoad() { //uni-app生命周期函数
            this.requestData()
        },
        methods: {
            // 刷新页面数据
            requestData() {
                tip.showLoading()
                //将多个请求接口添加到数组中
                const promises = [this.getRealSales(), this.getMessage(), this.getVideo(), this.getTemplate(), this.getTask(), this
                    .getUomMap()
                ]
                Promise.all(promises)
                    .then(res => {})
                    .catch(err => {})
                    .finally(() => {
                        tip.hideLoading()
                    })
            },
            //共需要请求6个接口,此处只取出一个接口做说明
            getRealSales() {// 获取实时销售数据
                return this.$api
                    .getRealSales()
                    .then(res => {
                        this.real_sales = tool.filterObjProp(res, ['sales_amount', 'passenger_flow', 'each_profit'])
                    })
                    .catch(err => tip.showToast(err))
            },
        }

你可以选择在拿到全部接口数据后统一做处理,也可以分别在每个接口返回后做处理,然后Promise.all中只是有一个空实现。

使用await操作符

await 操作符用于等待一个Promise 对象。它只能在异步函数 async function 中使用。

function test1 () {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('4s后,test1请求到了数据');
      resolve(123)
    }, 4000)
  })
}
function test2 (data) {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('2s后,test2请求到了数据');
      resolve(data * 1000)
    }, 2000)
  })
}

async function requestData () {
  try {
    console.log('我开始执行了');
    let response = await this.test1()
    response = await this.test2(response)
    console.log(response);
  } catch (e) {
    throw e
  } finally {
    console.log('我最后才会被执行');
  }
}
requestData()
/**
 * 我开始执行了
 * 4s后,test1请求到了数据
 * 2s后,test2请求到了数据
 * 123000
 * 我最后才会被执行
 */

如果一个 Promise 被传递给一个 await 操作符,await 将等待 Promise 正常处理完成并返回其处理结果。所以,如果接口直接有依赖关系,那么就可以使用await这种方式了。


   转载规则


《Promise的总结与使用》 刘星星 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
使用uni-app实现云打包和热更新 使用uni-app实现云打包和热更新
使用uni-app实现云打包和热更新云打包启动HBuilderX,然后点击“运行”–>“原生APP-云打包”,弹出如下页面: 如果你要同时发布Android和iOS,那么需要都需要配置其内容。需要注意的是,一般我们都需要将
2019-08-27
下一篇 
微信小程序总结提高 微信小程序总结提高
微信小程序总结提高现如今,微信小程序的开发越来越受欢迎,最近一段时间先后开发和维护了几个微信小程序,既有原生的,也有使用框架开发的.我现在维护的项目使用的框架有mpvue(现在已经迁移到uni-app)上,uni-app框架,Tar
  目录